TS

235 阅读4分钟

一、基础概念

1. TypeScript 和 JavaScript 的核心区别?

答案

  • TypeScript 是 JavaScript 的超集,添加了静态类型系统(编译时类型检查)。
  • TS 支持 ES6+ 特性,并通过接口、泛型、装饰器等增强代码可维护性。
  • TS 需编译为 JS 才能在浏览器中运行。

2. interfacetype 的区别?

答案

  • 相同点:都能定义对象、函数类型。
  • 不同点
    • 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. keyoftypeof 的作用?

答案

  • 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”的出现场景及处理方式?

答案

  • 场景:函数抛出错误或无限循环。
  • 处理:检查代码分支覆盖(如 switchdefault 处理)。
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>[];
}