React + TypeScript 笔试题库
核心技术栈: 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) => {