核心技术栈: UmiMax + React + Ant Design Pro + TypeScript
其他技术: Dva/Redux、ProComponents、Ahooks、Ant Design Charts
目录
- TypeScript 基础
- TypeScript 高级类型
- React + TypeScript
- UmiMax 相关
- Ant Design Pro 相关
- 泛型与工具类型
- 类型体操
- 实战编程题
- 架构设计题
TypeScript 基础
题目 1:类型推断
// 问:以下代码中,变量的类型是什么?
const a = 123;
const b = 'hello';
const c = [1, 2, 3];
const d = {
name: 'Alice', age: 20 };
const e = [1, 'hello', true];
// 答案
// a: number
// b: string
// c: number[]
// d: { name: string; age: number; }
// e: (string | number | boolean)[]
题目 2:interface vs type
问:以下代码有什么问题?如何修复?
// 问题代码
type User = {
name: string;
age: number;
};
type User = {
email: string;
};
// 答案:type 不能重复声明
// 修复方案 1:使用 interface
interface User {
name: string;
age: number;
}
interface User {
email: string;
}
// 结果:User { name: string; age: number; email: string; }
// 修复方案 2:使用交叉类型
type User = {
name: string;
age: number;
} & {
email: string;
};
题目 3:函数重载
实现一个函数,根据参数类型返回不同的结果
// 要求实现
function getValue(key: string): string;
function getValue(key: number): number;
function getValue(key: boolean): boolean;
// 答案
function getValue(key: string): string;
function getValue(key: number): number;
function getValue(key: boolean): boolean;
function getValue(key: string | number | boolean): string | number | boolean {
if (typeof key === 'string') {
return `String: ${
key}`;
} else if (typeof key === 'number') {
return key * 2;
} else {
return !key;
}
}
// 测试
console.log(getValue('hello')); // "String: hello"
console.log(getValue(10)); // 20
console.log(getValue(true)); // false
题目 4:枚举类型
问:以下两种枚举有什么区别?
// 数字枚举
enum Status1 {
Pending,
Success,
Failed,
}
// 字符串枚举
enum Status2 {
Pending = 'PENDING',
Success = 'SUCCESS',
Failed = 'FAILED',
}
// 答案:
// 1. 数字枚举支持反向映射,字符串枚举不支持
console.log(Status1[0]); // "Pending"
console.log(Status2['PENDING']); // undefined
// 2. 数字枚举会自增,字符串枚举需要手动赋值
// 3. 字符串枚举更易调试(查看网络请求时更清晰)
// 4. 推荐使用字符串枚举或 const enum
题目 5:类型守卫
实现一个类型守卫函数判断是否为字符串数组
// 实现 isStringArray
function processData(data: unknown) {
if (isStringArray(data)) {
// 这里 data 应该被推断为 string[]
data.forEach((item) => console.log(item.toUpperCase()));
}
}
// 答案
function isStringArray(value: unknown): value is string[] {
return (
Array.isArray(value) &&
value.every((item) => typeof item === 'string')
);
}
// 测试
processData(['a', 'b', 'c']); // 正常执行
processData([1, 2, 3]); // 不会执行
TypeScript 高级类型
题目 6:联合类型与交叉类型
问:以下代码的输出类型是什么?
type A = {
name: string; age: number };
type B = {
name: string; email: string };
type C = A & B;
type D = A | B;
const c: C = {
name: 'Alice',
age: 20,
email: 'alice@example.com',
};
const d1: D = {
name: 'Bob', age: 25 };
const d2: D = {
name: 'Charlie', email: 'charlie@example.com' };
// 答案:
// C = { name: string; age: number; email: string } (交叉类型,包含所有属性)
// D = A | B (联合类型,可以是 A 或 B)
题目 7:映射类型
实现一个将所有属性变为可选的工具类型
// 要求实现 MyPartial
type MyPartial<T> = ?;
// 测试
interface User {
id: number;
name: string;
email: string;
}
type PartialUser = MyPartial<User>;
// 应该等价于
// { id?: number; name?: string; email?: string; }
// 答案
type MyPartial<T> = {
[P in keyof T]?: T[P];
};
// 进阶:实现深度可选
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
题目 8:条件类型
实现一个提取 Promise 返回值类型的工具
// 要求实现 UnwrapPromise
type UnwrapPromise<T> = ?;
// 测试
type A = UnwrapPromise<Promise<string>>; // string
type B = UnwrapPromise<Promise<number>>; // number
type C = UnwrapPromise<string>; // string
// 答案
type UnwrapPromise<T> = T extends Promise<infer R> ? R : T;
// 进阶:支持嵌套 Promise
type DeepUnwrapPromise<T> = T extends Promise<infer R>
? DeepUnwrapPromise<R>
: T;
type D = DeepUnwrapPromise<Promise<Promise<string>>>; // string
题目 9:infer 关键字
实现一个获取函数返回值类型的工具
// 要求实现 MyReturnType
type MyReturnType<T> = ?;
// 测试
function getUser() {
return {
name: 'Alice', age: 20 };
}
type User = MyReturnType<typeof getUser>;
// 应该是 { name: string; age: number }
// 答案
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 扩展:获取函数参数类型
type MyParameters<T> = T extends (...args: infer P) => any ? P : never;
function add(a: number, b: number): number {
return a + b;
}
type Params = MyParameters<typeof add>; // [number, number]
题目 10:模板字面量类型
实现一个将字符串首字母大写的类型
// 要求实现 Capitalize
type Capitalize<S extends string> = ?;
// 测试
type A = Capitalize<'hello'>; // 'Hello'
type B = Capitalize<'world'>; // 'World'
// 答案
type Capitalize<S extends string> = S extends `${
infer First}${
infer Rest}`
? `${
Uppercase<First>}${
Rest}`
: S;
// 进阶:实现驼峰命名转换
type CamelCase<S extends string> = S extends `${
infer Head}_${
infer Tail}`
? `${
Head}${
Capitalize<CamelCase<Tail>>}`
: S;
type C = CamelCase<'user_name'>; // 'userName'
type D = CamelCase<'get_user_by_id'>; // 'getUserById'
React + TypeScript
题目 11:React 组件类型
定义一个 Button 组件的 Props 类型
// 要求:
// 1. 支持所有原生 button 属性
// 2. 添加自定义 loading 属性
// 3. children 必填
// 答案
import React from 'react';
interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
loading?: boolean;
children: React.ReactNode;
}
const Button: React.FC<ButtonProps> = ({
loading,
children,
disabled,
...rest
}) => {
return (
<button disabled={
disabled || loading} {
...rest}>
{
loading ? 'Loading...' : children}
</button>
);
};
// 使用
<Button onClick={
() => {
}} loading={
true}>
Click me
</Button>
题目 12:useState 类型推断
问:以下代码有什么问题?如何修复?
// 问题代码
function UserProfile() {
const [user, setUser] = useState(null);
useEffect(() => {
fetchUser().then((data) => {
setUser(data); // data 可能是 User 类型
});
}, []);
return <div>{
user.name}</div>; // 错误:user 可能为 null
}
// 答案 1:使用类型断言
interface User {
name: string;
age: number;
}
const [user, setUser] = useState<User | null>(null);
return <div>{
user?.name}</div>; // 使用可选链
// 答案 2:使用默认值
const [user, setUser] = useState<User>({
name: '',
age: 0,
});
// 答案 3:使用类型守卫
if (!user) return <div>Loading...</div>;
return <div>{
user.name}</div>;
题目 13:useRef 类型
实现一个自动聚焦的输入框组件
// 答案
import React, {
useRef, useEffect } from 'react';
const AutoFocusInput: React.FC = () => {
// 方法 1:使用泛型
const inputRef = useRef<HTMLInputElement>(null);
// 方法 2:使用类型断言
// const inputRef = useRef<HTMLInputElement>(null!);
useEffect(() => {
// null 检查
if (inputRef.current) {
inputRef.current.focus();
}
// 或使用可选链
inputRef.current?.focus();
}, []);
return <input ref={
inputRef} type="text" />;
};
// 进阶:转发 ref
interface InputProps {
placeholder?: string;
}
const ForwardedInput = React.forwardRef<HTMLInputElement, InputProps>(
({
placeholder }, ref) => {


1580

被折叠的 条评论
为什么被折叠?



