一、基础概念
1. TypeScript 和 JavaScript 的核心区别?
答案:
- TypeScript 是 JavaScript 的超集,添加了静态类型系统(编译时类型检查)。
- TS 支持 ES6+ 特性,并通过接口、泛型、装饰器等增强代码可维护性。
- TS 需编译为 JS 才能在浏览器中运行。
2. interface 和 type 的区别?
答案:
- 相同点:都能定义对象、函数类型。
- 不同点:
interface支持声明合并(多次定义自动合并),type不可重复。type能定义联合类型、交叉类型、元组等。interface更适合面向对象编程(类实现接口)。
// interface 合并
interface User { name: string; }
interface User { age: number; } // 自动合并
// type 定义联合类型
type Status = "success" | "error";
二、泛型(Generics)
3. 泛型的作用及使用场景?
答案:
- 用于创建可复用的组件,支持多种类型而不丢失类型安全。
- 场景:函数参数与返回值类型一致、API 响应封装、集合类。
function identity<T>(arg: T): T { return arg; }
const result = identity<string>("hello");
4. 泛型约束(Generic Constraints)如何实现?
答案:
通过 extends 限制泛型参数的类型范围,确保其具有特定属性或方法。
interface HasLength { length: number; }
function logLength<T extends HasLength>(arg: T): void {
console.log(arg.length);
}
logLength("hello"); // 5
5. 泛型默认类型(Default Generic Types)示例
答案:
为泛型参数提供默认类型,简化类型声明。
interface PaginationProps<T = number> {
current: T;
total: T;
}
const pagination: PaginationProps = { current: 1, total: 10 }; // T 默认为 number
三、接口(Interface)
6. 接口如何定义函数类型?
答案:
描述函数的参数类型和返回值类型。
interface SearchFunc {
(source: string, keyword: string): boolean;
}
const mySearch: SearchFunc = (src, kw) => src.includes(kw);
7. 接口的可选属性和只读属性如何声明?
答案:
- 可选属性:
? - 只读属性:
readonly
interface User {
readonly id: number; // 不可修改
name: string;
age?: number; // 可选
}
8. 接口如何继承其他类型?
答案:
- 接口通过
extends继承其他接口(支持多继承)。 - 类型别名通过交叉类型(
&)合并。
interface Animal { name: string; }
interface Dog extends Animal { breed: string; }
type Cat = Animal & { color: string };
四、类型系统与高级特性
9. 类型守卫(Type Guard)的实现方式?
答案:
typeof/instanceof- 自定义函数(返回类型谓词
arg is Type)。
function isString(value: any): value is string {
return typeof value === "string";
}
if (isString(input)) { input.toUpperCase(); } // 类型推断为 string
10. 条件类型(Conditional Types)是什么?
答案:
根据条件选择类型的语法:T extends U ? X : Y。
type NonNullable<T> = T extends null | undefined ? never : T;
type ValidString = NonNullable<string | null>; // string
11. keyof 和 typeof 的作用?
答案:
keyof T:获取T的所有键的联合类型。typeof:获取变量或对象的类型。
const colors = { red: "#FF0000", green: "#00FF00" };
type ColorKeys = keyof typeof colors; // "red" | "green"
五、React 集成
12. React 组件如何定义 Props 和 State 类型?
答案:
- 类组件:
React.Component<Props, State> - 函数组件:直接定义 Props 或使用
FC泛型。
// 函数组件
interface Props { message: string; }
const MyComponent: React.FC<Props> = ({ message }) => <div>{message}</div>;
13. React 泛型组件示例
答案:
处理动态数据类型的组件。
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <div>{items.map(renderItem)}</div>;
}
六、工具类型与实用技巧
14. Partial<T>、Required<T>、Pick<T, K> 的作用?
答案:
Partial<T>:所有属性变为可选。Required<T>:所有属性变为必填。Pick<T, K>:选取指定属性。
interface User { name: string; age?: number; }
type PartialUser = Partial<User>; // { name?: string; age?: number; }
15. Record<K, T> 和 Omit<T, K> 的使用场景?
答案:
Record<K, T>:创建键为K、值为T的对象类型。Omit<T, K>:剔除指定属性。
type ColorMap = Record<"red" | "green", string>; // { red: string; green: string; }
type SimpleUser = Omit<User, "age">; // { name: string; }
七、调试与错误处理
16. 如何处理“类型 'undefined' 不能赋值给类型 'string'”错误?
答案:
- 使用非空断言(
!)或可选链(?.)。 - 显式检查或设置默认值。
const name = user.name!; // 非空断言
const age = user.age ?? 18; // 默认值
17. 类型“never”的出现场景及处理方式?
答案:
- 场景:函数抛出错误或无限循环。
- 处理:检查代码分支覆盖(如
switch的default处理)。
function error(message: string): never {
throw new Error(message);
}
八、复杂场景与设计
18. 如何用 TS 实现类型安全的 EventEmitter?
答案:
结合泛型和映射类型定义事件与回调关系。
interface EventMap {
click: (x: number, y: number) => void;
}
class EventEmitter<T extends EventMap> {
on<K extends keyof T>(event: K, listener: T[K]) { /*...*/ }
emit<K extends keyof T>(event: K, ...args: Parameters<T[K]>) { /*...*/ }
}
19. 如何处理递归类型(如树形结构)?
答案:
通过接口自身引用定义递归结构。
interface TreeNode<T> {
value: T;
children?: TreeNode<T>[];
}