TypeScript
一、类型系统(基础类型、联合类型、交叉类型、类型别名)
1. TypeScript 有哪些基础类型?
定义: TypeScript 基础类型是类型系统的基石,分为原始类型、对象类型和特殊类型三大类。
原理: TypeScript 在 JavaScript 的运行时类型之上添加了编译时静态类型层。类型注解在编译后会被擦除,不影响运行时行为。
typescript
// 原始类型
let isDone: boolean = false;
let age: number = 25;
let name: string = "TypeScript";
let n: null = null;
let u: undefined = undefined;
let s: symbol = Symbol("id");
let big: bigint = 100n;
// 数组
let nums: number[] = [1, 2, 3];
let strs: Array<string> = ["a", "b"];
// 元组
let tuple: [string, number] = ["hello", 42];
// 枚举
enum Color { Red, Green, Blue }
常见误区:
object不包括原始类型,只表示非原始类型number包含整数和浮点数- 启用
strictNullChecks后,null和undefined只能赋给any、unknown或自身 bigint是 ES2020 引入的大整数类型
2. 什么是联合类型?使用场景有哪些?
定义: 联合类型(Union Types)使用 | 分隔多个类型,表示值可以是其中任意一种类型。
原理: 联合类型创建多个类型的并集。在访问属性时,TypeScript 只允许访问所有成员共有的属性,需要通过类型守卫来缩小类型范围。
typescript
// 基础联合类型
let id: string | number;
id = "abc";
id = 123;
// 使用场景:字面量联合
type Direction = "up" | "down" | "left" | "right";
function move(dir: Direction) { /* ... */ }
// 使用场景:多种对象类型
interface Cat { meow(): void; name: string; }
interface Dog { bark(): void; name: string; }
function greet(pet: Cat | Dog) {
console.log(pet.name); // OK: 共有属性
// pet.meow(); // Error: 不是共有属性
}
// 配合类型守卫使用
function handle(value: string | number) {
if (typeof value === "string") {
value.toUpperCase(); // 窄化为 string
} else {
value.toFixed(2); // 窄化为 number
}
}
常见误区:
- 联合类型只能安全访问所有成员共有的属性和方法
- 字面量联合类型比枚举更轻量,推荐使用
- 函数参数的联合类型需要通过类型守卫区分处理
3. 什么是交叉类型?使用场景有哪些?
定义: 交叉类型(Intersection Types)使用 & 分隔多个类型,表示值必须同时具备所有成员类型的特性。
原理: 交叉类型将多个类型的成员合并为一个新类型,常用于对象类型的组合和扩展。
typescript
// 基础交叉类型
interface Nameable { name: string; }
interface Ageable { age: number; }
type Person = Nameable & Ageable;
// 等价于 { name: string; age: number; }
const person: Person = { name: "Alice", age: 30 };
// 使用场景:对象类型扩展
interface User { id: number; name: string; }
interface Admin { role: string; permissions: string[]; }
type AdminUser = User & Admin;
// { id: number; name: string; role: string; permissions: string[] }
// 使用场景:方法组合
interface Loggable { log(): void; }
interface Saveable { save(): void; }
type Persistable = Loggable & Saveable;
常见误区:
- 交叉类型不等同于类的多继承
- 如果两个类型有同名但不同类型的属性,交叉后该属性类型为
never - 交叉类型主要用于对象类型的组合
4. 联合类型与交叉类型的区别
对比分析:
| 维度 | 联合类型 A | B | 交叉类型 A & B | |------|-----------------|------------------| | 语义 | 取其一(OR) | 同时具备(AND) | | 符号 | | | & | | 赋值 | 满足任一类型即可 | 必须满足所有类型 | | 属性访问 | 只能访问共有成员 | 可访问所有成员 | | 类比 | 集合的并集 | 集合的交集 | | 典型场景 | 函数参数多态、字面量枚举 | 对象组合、Mixin 模式 |
typescript
// 联合类型:可能是多种类型之一
type Result = { ok: true; data: string } | { ok: false; error: string };
function process(r: Result) {
if (r.ok) {
r.data; // 窄化后访问
}
}
// 交叉类型:同时具备多种类型的特性
type WithId = { id: string };
type WithTimestamp = { createdAt: Date; updatedAt: Date };
type Entity = WithId & WithTimestamp;
选择策略:
- 函数参数接受多种可能类型 → 联合类型
- 对象需要组合多个接口/类型 → 交叉类型
- 字面量类型限制取值范围 → 联合类型
- 在现有类型上扩展属性 → 交叉类型
5. TypeScript 类型系统的特性有哪些?
核心特性:
- 静态类型检查: 编译时验证类型,而非运行时
- 结构化类型(Duck Typing): 兼容性基于结构而非名称
- 类型推断: 编译器自动推断类型
- 类型守卫: 运行时判断 + 编译时窄化
- 泛型支持: 类型参数化
- 联合/交叉类型: 灵活的类型组合
- 装饰器: 元编程能力
- 映射类型/条件类型: 高级类型变换
- 声明合并: 同名声明自动合并
- 类型别名:
type关键字创建别名
typescript
// 结构化类型示例:基于结构兼容
interface A { x: number; y: number; }
interface B { x: number; y: number; }
let a: A = { x: 1, y: 2 };
let b: B = a; // OK: 结构相同
6. 数据类型与存储机制(堆栈区别)
原理: JavaScript 中的数据类型分为值类型(原始类型)和引用类型,分别存储在栈内存和堆内存中。
| 特性 | 栈内存(值类型) | 堆内存(引用类型) |
|---|---|---|
| 存储内容 | 实际值 | 内存地址引用 |
| 大小 | 固定 | 动态可变 |
| 访问速度 | 快 | 较慢 |
| 垃圾回收 | 自动清理 | GC 管理 |
| 赋值行为 | 值拷贝 | 地址拷贝 |
| 数据类型 | boolean/number/string/null/undefined/symbol/bigint | object/array/function/class |
typescript
// 值类型:栈存储
let a = 10;
let b = a; // 值拷贝
b = 20;
console.log(a); // 10,互不影响
// 引用类型:堆存储
let obj1 = { x: 1 };
let obj2 = obj1; // 地址拷贝
obj2.x = 2;
console.log(obj1.x); // 2,互相影响
7. 类型别名的定义方式
定义: 类型别名使用 type 关键字给任何类型创建一个新名称。
typescript
// 基本类型别名
type ID = string | number;
// 对象类型别名
type User = {
id: number;
name: string;
email?: string;
};
// 联合类型别名
type Status = "pending" | "success" | "error";
// 泛型别名
type Result<T> = { ok: true; data: T } | { ok: false; error: string };
// 交叉类型别名
type Admin = User & { role: string };
// 映射类型别名
type Optional<T> = { [P in keyof T]?: T[P] };
8. type 与 interface 的区别
对比分析:
| 维度 | interface |
type |
|---|---|---|
| 声明合并 | 支持(同名自动合并) | 不支持 |
| 扩展语法 | extends |
& 交叉类型 |
| 支持范围 | 对象/函数/数组/类 | 所有类型(联合/交叉/元组/字面量/映射等) |
| 类实现 | implements 支持 |
不支持直接 implements |
| 重复声明 | 允许(自动合并) | 不允许(重复定义报错) |
| 计算属性 | 不支持 | 支持映射类型 |
typescript
// interface 声明合并
interface User { id: number; }
interface User { name: string; }
// 合并为 { id: number; name: string }
// type 不能合并
type User = { id: number; }
// type User = { name: string; } // Error: 重复定义
// interface 只能定义对象形状
interface Point { x: number; y: number; }
// type 可以定义任何类型
type Point = { x: number; y: number };
type ID = string | number;
type Pair = [number, number];
type Callback<T> = (data: T) => void;
选择策略:
- 定义对象形状、供类实现、需要声明合并时 → 使用
interface - 定义联合/交叉/元组/字面量类型、需要映射类型时 → 使用
type - 团队协作建议统一约定
二、接口(Interface)与类型别名
9. TypeScript 接口的定义与用途
定义: 接口(Interface)用于定义对象的结构契约,规定对象必须具有哪些属性和方法。
typescript
// 基本接口
interface User {
id: number;
name: string;
email: string;
}
// 函数类型接口
interface SearchFunc {
(source: string, subString: string): boolean;
}
let search: SearchFunc = (src, sub) => src.includes(sub);
// 数组类型接口
interface StringArray {
[index: number]: string;
}
let arr: StringArray = ["a", "b"];
// 类实现接口
interface ClockInterface {
currentTime: Date;
setTime(d: Date): void;
}
class Clock implements ClockInterface {
currentTime: Date = new Date();
setTime(d: Date) { this.currentTime = d; }
}
接口用途:
- 定义对象结构,提供类型安全
- 作为类实现的契约
- 函数参数类型约束
- API 响应结构定义
- 模块间通信的类型约定
10. 接口可以包含哪些成员?
typescript
interface FullInterface {
// 必需属性
id: number;
name: string;
// 可选属性
email?: string;
// 只读属性
readonly createdAt: Date;
// 方法
greet(): void;
add(a: number, b: number): number;
// 索引签名
[key: string]: any;
// 调用签名(接口自身可调用)
(x: number): string;
// 构造签名(创建实例)
new (x: number): object;
}
11. 接口继承与扩展
原理: 接口通过 extends 关键字继承其他接口,实现类型复用和组合。
typescript
// 单继承
interface Animal { name: string; }
interface Dog extends Animal { bark(): void; }
// 多继承
interface Flyable { fly(): void; }
interface Swimmable { swim(): void; }
interface Duck extends Animal, Flyable, Swimmable {}
// 接口继承类型别名
type Point = { x: number; y: number };
interface Point3D extends Point { z: number; }
12. 接口合并(声明合并)
定义: 同名接口会自动合并,这是 TypeScript 声明合并的一种形式。
typescript
interface Document {
title: string;
}
interface Document {
author: string;
}
// 合并后等价于
// interface Document { title: string; author: string; }
let doc: Document = { title: "TS Guide", author: "Alice" };
合并规则:
- 同名非函数成员:类型必须一致,否则报错
- 同名函数成员:形成重载
- 命名空间与类/函数/枚举可以合并
13. 索引签名 / 可索引类型
typescript
// 字符串索引签名
interface StringDict {
[key: string]: number;
}
let dict: StringDict = { a: 1, b: 2 };
// 数字索引签名
interface StringArray {
[index: number]: string;
}
let arr: StringArray = ["a", "b"];
// 联合索引
interface MixedDict {
[key: string]: number | string;
}
// 注意:索引签名会限制其他属性的类型
interface BadDict {
[key: string]: number;
name: string; // Error: string 不兼容 number
}
14. 可选属性与只读属性
typescript
// 可选属性
interface Config {
host: string;
port?: number;
timeout?: number;
}
// 只读属性
interface Point {
readonly x: number;
readonly y: number;
}
let p: Point = { x: 1, y: 2 };
// p.x = 3; // Error: 只读属性不可修改
// 只读数组
let arr: ReadonlyArray<number> = [1, 2, 3];
// arr.push(4); // Error
三、泛型(Generics)
15. 什么是泛型?泛型的用途
定义: 泛型允许在定义函数、接口或类时不指定具体类型,而是用类型参数代替,在使用时再指定具体类型。
原理: 泛型通过类型参数将类型抽象化,实现类型安全的代码复用,避免使用 any 丢失类型信息。
typescript
// 泛型函数
function identity<T>(arg: T): T {
return arg;
}
let output1 = identity<string>("hello");
let output2 = identity(42); // 推断为 number
// 泛型接口
interface Result<T> {
code: number;
data: T;
message: string;
}
// 泛型类
class Stack<T> {
private items: T[] = [];
push(item: T) { this.items.push(item); }
pop(): T | undefined { return this.items.pop(); }
}
// 多泛型参数
function pair<T, U>(a: T, b: U): [T, U] {
return [a, b];
}
let p = pair("hello", 42); // [string, number]
用途:
- 组件/函数复用,同一逻辑适配多种类型
- 保持类型安全,避免使用
any - 确保参数间类型关系的一致性
- 构建可复用的工具函数和数据结构
16. 泛型约束
原理: 使用 extends 关键字限制泛型参数的范围,确保类型参数具有特定属性或结构。
typescript
// 基本约束
interface Lengthwise { length: number; }
function loggingIdentity<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
loggingIdentity("hello"); // OK
// loggingIdentity(123); // Error: number 没有 length
// 使用 keyof 约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
let obj = { a: 1, b: 2, c: 3 };
getProperty(obj, "a"); // OK
// getProperty(obj, "x"); // Error
// 构造函数约束
function createInstance<T extends { new(...args: any[]): {} }>(ctor: T): T {
return new ctor();
}
17. 泛型默认值
typescript
// 接口默认值
interface Response<T = any> {
status: number;
data: T;
}
// 函数默认值
function createArray<T = string>(length: number, value: T): T[] {
return Array(length).fill(value);
}
let arr = createArray(3, "x"); // string[]
// 类型别名默认值
type Result<T = unknown> = { ok: true; data: T } | { ok: false; error: string };
四、装饰器(Decorators)
18. 什么是装饰器?装饰器的作用
定义: 装饰器是一种特殊类型的声明,能够被附加到类声明、方法、属性或参数上,用于修改类的行为或添加元数据。
原理: 装饰器本质上是函数,在类定义时被调用。需要在 tsconfig.json 中启用 experimentalDecorators。
json
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
装饰器分类:
- 类装饰器
- 方法装饰器
- 属性装饰器
- 参数装饰器
- 访问器装饰器
19. 类装饰器
typescript
// 基本类装饰器
function sealed(constructor: Function) {
Object.seal(constructor);
Object.seal(constructor.prototype);
}
@sealed
class Greeter {
greeting: string;
constructor(message: string) {
this.greeting = message;
}
}
// 返回新构造函数的装饰器
function classDecorator<T extends { new(...args: any[]): {} }>(constructor: T) {
return class extends constructor {
newProperty = "new property";
};
}
@classDecorator
class MyClass {
hello = "hello";
}
20. 方法装饰器
typescript
function log(
target: any,
propertyKey: string,
descriptor: PropertyDescriptor
) {
const original = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`调用 ${propertyKey},参数:${JSON.stringify(args)}`);
const result = original.apply(this, args);
console.log(`返回值:${JSON.stringify(result)}`);
return result;
};
return descriptor;
}
class Calculator {
@log
add(a: number, b: number): number {
return a + b;
}
}
21. 属性装饰器
typescript
function Min(min: number) {
return function (target: any, propertyKey: string) {
let value: number;
Object.defineProperty(target, propertyKey, {
get: () => value,
set(newVal: number) {
if (newVal < min) {
throw new Error(`${propertyKey} 不能小于 ${min}`);
}
value = newVal;
}
});
};
}
class Product {
@Min(0)
price: number;
}
22. 参数装饰器
typescript
function Required(target: any, propertyKey: string, parameterIndex: number) {
console.log(`类: ${target.constructor.name}, 方法: ${propertyKey}, 参数索引: ${parameterIndex}`);
}
class Service {
login(@Required username: string, @Required password: string) {
// ...
}
}
23. 装饰器工厂与执行顺序
装饰器工厂: 返回装饰器函数的函数,可以传递参数。
typescript
function Color(value: string) {
return function (target: any) {
target.color = value;
};
}
@Color("red")
class Car {}
执行顺序:
- 属性装饰器 > 方法装饰器 > 方法参数装饰器 > 类装饰器
- 同一类型的装饰器:从右到左、从下到上执行
typescript
function f(key: string) {
console.log(`${key} evaluated`);
return function (target: any, propertyKey: string) {
console.log(`${key} called`);
};
}
class Demo {
@f("A")
@f("B")
method() {}
}
// 输出:
// A evaluated
// B evaluated
// B called
// A called
24. 装饰器应用场景
- 日志记录: 方法调用前后日志
- 权限校验: 路由/方法访问控制
- 缓存: 方法结果缓存(Memo 化)
- 依赖注入: Angular 框架核心机制
- 数据验证: DTO 字段验证(class-validator)
- 元数据反射: 存储类型信息
- Mixin 模式: 类功能组合
五、模块系统
25. TypeScript 模块
定义: TypeScript 模块分为外部模块(ES Modules / CommonJS)和内部模块(已废弃,使用命名空间替代)。
typescript
// ES Module 导出 (math.ts)
export const PI = 3.14159;
export function add(a: number, b: number): number {
return a + b;
}
export default class Calculator {}
// 导入 (app.ts)
import Calculator, { PI, add } from "./math";
import * as Math from "./math";
26. 模块导出与导入的语法
typescript
// 命名导出
export { foo, bar };
export { foo as aliasFoo };
// 默认导出
export default function fn() {}
export default class MyClass {}
// 重新导出
export { foo } from "./module";
export * from "./module";
// 类型导入(TS 4.5+,编译时被移除)
import { type Foo } from "./module";
// CommonJS 导出
export = { foo: 1, bar: 2 };
// CommonJS 导入
import foo = require("./module");
27. CommonJS 与 ES6 模块的区别
| 维度 | CommonJS | ES6 模块 |
|---|---|---|
| 关键字 | require() / module.exports |
import / export |
| 加载时机 | 运行时加载 | 编译时静态分析 |
| 输出拷贝 | 值拷贝(浅拷贝) | 值引用 |
| 循环依赖 | 可能返回不完整值 | 自动处理 |
| Tree-shaking | 不支持 | 支持 |
| 默认导出 | 无 | export default |
typescript
// CommonJS
const express = require("express");
module.exports = { foo: 1 };
// ES Module
import express from "express";
export { foo: 1 };
28. 命名空间
typescript
// 命名空间定义
namespace Validation {
export interface Validator {
isValid(s: string): boolean;
}
export class EmailValidator implements Validator {
isValid(s: string): boolean {
return s.includes("@");
}
}
}
// 使用
let validator: Validation.Validator = new Validation.EmailValidator();
// 跨文件命名空间
/// <reference path="validation.ts" />
namespace Validation {
export class LengthValidator implements Validator { /* ... */ }
}
29. 模块与命名空间的区别
| 维度 | 模块 | 命名空间 |
|---|---|---|
| 文件关系 | 一个文件即一个模块 | 可在单个或多个文件中定义 |
| 作用域 | 文件级隔离 | 全局/嵌套作用域 |
| 加载方式 | 需要模块加载器 | script 标签直接加载 |
| 推荐度 | 推荐使用 | 仅用于声明文件或遗留代码 |
| 打包支持 | 支持 tree-shaking | 不支持 |
最佳实践: 优先使用模块,命名空间仅用于 .d.ts 声明文件。
30. 声明文件(.d.ts)
定义: 声明文件用于描述 JavaScript 库的类型信息,供 TypeScript 编译器使用。
typescript
// 全局声明
declare var jQuery: (selector: string) => any;
// 模块声明
declare module "my-module" {
export function foo(): string;
export const VERSION: string;
}
// 声明类
declare class MyClass {
constructor(name: string);
greet(): void;
}
// 声明函数重载
declare function format(fmt: string): string;
declare function format(fmt: string, ...args: any[]): string;
31. 声明合并
typescript
// 接口合并
interface Box { width: number; }
interface Box { height: number; }
// 合并为 { width: number; height: number }
// 命名空间合并
namespace Animals { export class Cat {} }
namespace Animals { export class Dog {} }
// 函数与命名空间合并
function greet(name: string): void;
namespace greet {
export const defaultGreeting = "Hello";
}
六、类型推断与类型断言
32. 类型推断的工作原理
定义: TypeScript 编译器在没有显式类型注解时,根据上下文自动推断出类型。
推断规则:
- 变量推断: 根据初始值推断
- 函数返回值推断: 根据 return 语句推断
- 上下文推断: 根据使用场景推断
- 泛型推断: 根据传入参数推断
typescript
// 变量推断
let x = 3; // 推断为 number
let y = "hello"; // 推断为 string
// 最佳通用类型推断
let arr = [1, null, "three"]; // (number | null | string)[]
// 上下文类型推断
window.onmousedown = function (mouseEvent) {
// mouseEvent 推断为 MouseEvent
console.log(mouseEvent.button);
};
// 函数返回值推断
function add(a: number, b: number) {
return a + b; // 推断返回 number
}
// 泛型推断
function identity<T>(arg: T): T { return arg; }
let output = identity("hello"); // T 推断为 string
33. 什么是类型断言?使用场景
定义: 类型断言告诉编译器"相信我,这个值就是该类型",跳过编译器的类型检查。
语法:
typescript
// as 语法(推荐,JSX 友好)
let someValue: unknown = "this is a string";
let strLength = (someValue as string).length;
// 尖括号语法(不能用于 JSX)
let strLength2 = (<string>someValue).length;
使用场景:
- DOM 元素类型转换
- 解析 JSON 数据
- 第三方库返回类型转换
- 测试 mock 数据
34. as 语法 / as string 语法的作用
typescript
// DOM 元素类型转换
let elem = document.getElementById("app") as HTMLDivElement;
// 解析 JSON
let data = JSON.parse('{"name": "Alice"}') as { name: string };
// 常量断言(as const)
let x = "hello" as const; // 类型为 "hello"
let y = [10, 20] as const; // 类型为 readonly [10, 20]
let z = { text: "hello" } as const; // 类型为 { readonly text: "hello" }
35. 非空断言与双重断言
typescript
// 非空断言操作符 !
function liveDangerously(x?: number | null) {
console.log(x!.toFixed()); // 告诉编译器 x 不为 null/undefined
}
// 双重断言(绕过类型检查,慎用)
const handler = (event: Event) => {
const mouseEvent = event as any as MouseEvent;
}
// satisfies 运算符(TS 4.9+)
const config = {
host: "localhost",
port: 3000
} satisfies Record<string, string | number>;
// config.host 保留精确类型
常见误区:
- 类型断言不会改变运行时行为
- 双重断言可能隐藏类型错误,应谨慎使用
as const用于创建只读的字面量类型!非空断言在运行时不会检查实际值
36. 什么是类型守卫?
定义: 类型守卫是运行时检查,用于确保变量在某个作用域内具有特定类型,编译器据此进行类型窄化。
类型守卫方式:
typescript
// typeof 类型守卫
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return " ".repeat(padding) + value; // 窄化为 number
}
return padding + value; // 窄化为 string
}
// instanceof 类型守卫
class Bird { fly() {} }
class Fish { swim() {} }
function move(pet: Bird | Fish) {
if (pet instanceof Bird) {
pet.fly();
} else {
pet.swim();
}
}
// in 操作符类型守卫
interface Bird { fly(): void; }
interface Fish { swim(): void; }
function handle(pet: Bird | Fish) {
if ("swim" in pet) {
pet.swim(); // 窄化为 Fish
}
}
// 自定义类型守卫(类型谓词)
function isFish(pet: Bird | Fish): pet is Fish {
return (pet as Fish).swim !== undefined;
}
if (isFish(pet)) {
pet.swim(); // 窄化为 Fish
}
37. 字面量类型的使用
typescript
// 字符串字面量类型
type Direction = "north" | "south" | "east" | "west";
// 数字字面量类型
type DiceRoll = 1 | 2 | 3 | 4 | 5 | 6;
// 布尔字面量类型
type TrueOnly = true;
// 组合使用(discriminated union)
type Event = { type: "click"; x: number; y: number }
| { type: "keydown"; key: string };
function handleEvent(event: Event) {
if (event.type === "click") {
console.log(event.x, event.y); // 窄化
}
}
七、工具类型(Utility Types)
38. TypeScript 工具类型概览
定义: 工具类型是 TypeScript 内置的泛型类型,用于常见的类型变换操作。
39. Partial<T> 工具类型的作用
作用: 将类型 T 的所有属性变为可选。
typescript
interface User { id: number; name: string; email: string; }
type PartialUser = Partial<User>;
// { id?: number; name?: string; email?: string }
// 使用场景:更新接口
function updateUser(id: number, updates: Partial<User>) {
// 只需要传递需要更新的字段
}
// 实现原理
type MyPartial<T> = { [P in keyof T]?: T[P] };
40. Required<T>、Readonly<T> 的用途
typescript
// Required: 所有属性变为必选
interface Config { host?: string; port?: number; }
type RequiredConfig = Required<Config>;
// { host: string; port: number }
// Readonly: 所有属性变为只读
interface Todo { title: string; completed: boolean; }
type ReadonlyTodo = Readonly<Todo>;
// { readonly title: string; readonly completed: boolean }
// 实现原理
type MyRequired<T> = { [P in keyof T]-?: T[P] };
type MyReadonly<T> = { readonly [P in keyof T]: T[P] };
41. Pick<T, K> 和 Omit<T, K> 的区别
typescript
// Pick: 从 T 中选择指定属性
interface Todo { title: string; description: string; completed: boolean; }
type TodoPreview = Pick<Todo, "title" | "completed">;
// { title: string; completed: boolean }
// Omit: 从 T 中排除指定属性
type TodoInfo = Omit<Todo, "completed">;
// { title: string; description: string }
// 实现原理
type MyPick<T, K extends keyof T> = { [P in K]: T[P] };
type MyOmit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
42. Exclude<T, U> 和 Extract<T, U> 的作用
typescript
// Exclude: 从 T 中排除可分配给 U 的类型
type T1 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T2 = Exclude<string | number, number>; // string
// Extract: 从 T 中提取可分配给 U 的类型
type T3 = Extract<"a" | "b" | "c", "a" | "d">; // "a"
type T4 = Extract<string | number, number>; // number
// 实现原理
type MyExclude<T, U> = T extends U ? never : T;
type MyExtract<T, U> = T extends U ? T : never;
43. NonNullable<T> 的作用
typescript
type T1 = NonNullable<string | number | undefined>; // string | number
type T2 = NonNullable<string[] | null | undefined>; // string[]
// 实现原理
type MyNonNullable<T> = T extends null | undefined ? never : T;
44. ReturnType<T> 获取什么类型?
作用: 获取函数 T 的返回值类型。
typescript
function getUser() {
return { id: 1, name: "Alice" };
}
type User = ReturnType<typeof getUser>;
// { id: number; name: string }
// 箭头函数
type Fn = () => { x: number };
type R = ReturnType<Fn>; // { x: number }
// 实现原理
type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
45. Record<K, T>
作用: 构造一个对象类型,键类型为 K,值类型为 T。
typescript
type PageInfo = { title: string; url: string };
type Pages = "home" | "about" | "contact";
type SiteMap = Record<Pages, PageInfo>;
const sitemap: SiteMap = {
home: { title: "Home", url: "/" },
about: { title: "About", url: "/about" },
contact: { title: "Contact", url: "/contact" }
};
46. Parameters<T>
作用: 获取函数 T 的参数类型组成的元组。
typescript
function greet(name: string, age: number) {}
type GreetParams = Parameters<typeof greet>;
// [name: string, age: number]
// 常用于包装函数
function withLogging<T extends (...args: any[]) => any>(
fn: T,
name: string
): (...args: Parameters<T>) => ReturnType<T> {
return (...args) => {
console.log(`调用 ${name}`);
return fn(...args);
};
}
47. InstanceType<T>
作用: 获取构造函数类型 T 的实例类型。
typescript
class C { x = 0; y = 0; }
type T1 = InstanceType<typeof C>; // C
type T2 = InstanceType<new () => { x: number }>; // { x: number }
48. ThisParameterType / OmitThisParameter / ThisType
typescript
// ThisParameterType: 获取函数的 this 参数类型
function toHex(this: Number) { return this.toString(16); }
type ThisType = ThisParameterType<typeof toHex>; // Number
// OmitThisParameter: 移除函数的 this 参数
type NoThis = OmitThisParameter<typeof toHex>; // () => string
// ThisType: 用于对象字面量中指定 this 类型
interface ObjectMethods {
methods: {
hello: () => string;
};
}
let obj: ObjectMethods & ThisType<{ name: string }> = {
methods: {
hello() { return `Hello, ${this.name}`; }
}
};
49. 字符串工具类型
typescript
type T1 = Uppercase<"hello">; // "HELLO"
type T2 = Lowercase<"HELLO">; // "hello"
type T3 = Capitalize<"hello">; // "Hello"
type T4 = Uncapitalize<"Hello">; // "hello"
八、TypeScript 配置与编译
50. tsconfig.json 配置文件
定义: tsconfig.json 是 TypeScript 项目的配置文件,指定编译选项和包含的文件。
json
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
"lib": ["ES2020", "DOM"],
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"moduleResolution": "node",
"declaration": true,
"sourceMap": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"],
"extends": "./tsconfig.base.json"
}
51. tsconfig.json 的重要配置项
| 配置项 | 说明 | 常用值 |
|---|---|---|
target |
编译目标 JS 版本 | ES5/ES6/ES2020/ESNext |
module |
模块系统 | CommonJS/ESNext/AMD |
lib |
包含的库类型声明 | ES2020/DOM/DOM.Iterable |
outDir |
输出目录 | ./dist |
rootDir |
源文件根目录 | ./src |
strict |
启用所有严格检查 | true |
noImplicitAny |
禁止隐式 any | true |
strictNullChecks |
严格空值检查 | true |
esModuleInterop |
兼容 CJS 和 ESM | true |
declaration |
生成 .d.ts 文件 | true |
sourceMap |
生成源映射 | true |
skipLibCheck |
跳过声明文件检查 | true |
moduleResolution |
模块解析策略 | node/node16/bundler |
resolveJsonModule |
导入 JSON 文件 | true |
52. strict 模式的作用
定义: strict 模式启用所有严格类型检查选项,是最佳实践的起点。
等价于同时启用:
strictNullChecks- 严格空值检查strictFunctionTypes- 严格函数类型检查strictBindCallApply- 严格 bind/call/apply 检查strictPropertyInitialization- 严格属性初始化检查noImplicitThis- 禁止隐式 this 为 anyalwaysStrict- 始终使用严格模式noImplicitAny- 禁止隐式 anyuseUnknownInCatchVariables- catch 变量为 unknown
53. esModuleInterop 配置的意义
定义: 兼容 CommonJS 和 ES 模块的互操作。
typescript
// esModuleInterop: true 时
import express from "express"; // OK: 允许默认导入 CommonJS
// esModuleInterop: false 时
import * as express from "express"; // 需要命名空间导入
原理: 为 CommonJS 模块创建命名空间对象,并允许 import default 语法。
54. target 与 module 选项
target: 指定编译目标 JavaScript 版本
| 值 | 说明 |
|---|---|
| ES3 | 老旧浏览器(已不常用) |
| ES5 | 现代浏览器兼容 |
| ES6/ES2015 | 支持 class、箭头函数等 |
| ES2020+ | 支持最新特性 |
module: 指定模块系统
| 值 | 说明 |
|---|---|
| CommonJS | Node.js 默认 |
| ESNext | ES 模块 |
| AMD | 异步模块定义 |
| UMD | 通用模块定义 |
55. include、exclude、files 与 extends
json
{
"files": ["src/index.ts", "src/app.ts"], // 精确指定
"include": ["src/**/*", "types/**/*"], // 通配符匹配
"exclude": ["node_modules", "dist", "**/*.test.ts"], // 排除
"extends": "./tsconfig.base.json" // 继承配置
}
优先级: files > include > exclude
九、枚举
56. TypeScript 枚举
定义: 枚举是一组命名常量的集合,提供有意义的名称替代魔法数字。
typescript
// 数字枚举
enum Direction {
Up, // 0
Down, // 1
Left, // 2
Right // 3
}
// 指定初始值
enum Direction2 {
Up = 1,
Down, // 2
Left, // 3
Right // 4
}
57. 字符串枚举与异构枚举
typescript
// 字符串枚举(推荐)
enum LogLevel {
DEBUG = "DEBUG",
INFO = "INFO",
WARN = "WARN",
ERROR = "ERROR"
}
// 无反向映射
// 异构枚举(不推荐)
enum Mixed {
No = 0,
Yes = "YES"
}
58. 反向映射与 const 枚举
typescript
// 反向映射(仅数字枚举)
enum Direction { Up = 1, Down }
console.log(Direction.Up); // 1
console.log(Direction[1]); // "Up" 反向映射
console.log(Direction["Down"]); // 2
// const 枚举(编译后内联,无运行时开销)
const enum Direction {
Up, Down, Left, Right
}
let d = Direction.Up;
// 编译为: var d = 0 /* Up */;
59. 枚举合并
typescript
enum Status { Active = "active" }
enum Status { Inactive = "inactive" }
// 合并为
// enum Status {
// Active = "active",
// Inactive = "inactive"
// }
十、特殊类型与高级类型
60. never、unknown、any、void 的区别
| 类型 | 含义 | 赋值能力 | 被赋值能力 | 使用场景 |
|---|---|---|---|---|
any |
任意类型 | 任意 | 任意 | 禁用类型检查(避免使用) |
unknown |
任意类型(安全) | any/unknown | 任意 | 安全接收未知类型 |
never |
永不存在的值 | 任意 | never | 穷尽检查、抛出异常 |
void |
无返回值 | undefined/null | 无 | 函数无返回值 |
typescript
// never: 穷尽检查
type Shape = { type: "circle"; r: number }
| { type: "square"; side: number }
| { type: "triangle"; base: number; height: number };
function area(shape: Shape): number {
switch (shape.type) {
case "circle": return Math.PI * shape.r ** 2;
case "square": return shape.side ** 2;
case "triangle": return 0.5 * shape.base * shape.height;
default:
const _exhaustive: never = shape;
return _exhaustive;
}
}
// unknown: 安全检查
function process(value: unknown) {
// value.toUpperCase(); // Error
if (typeof value === "string") {
value.toUpperCase(); // OK,已窄化
}
}
// any: 绕过检查(不推荐)
function risky(value: any) {
value.toUpperCase(); // 编译通过,运行可能报错
value.nonExistentMethod(); // 编译通过
}
61. null 与 undefined
typescript
// strictNullChecks: true 时
let a: null = null; // OK
let b: undefined = undefined; // OK
let c: string = null; // Error
let d: string | null = null; // OK
// 可选链与空值合并
let user = { profile: { name: "Alice" } };
let name = user?.profile?.name; // 可选链
let count = user?.count ?? 0; // 空值合并
62. object 类型
typescript
// object: 表示非原始类型
let obj: object = { a: 1 };
let fn: object = () => {};
// let num: object = 42; // Error: 原始类型
// {} 空对象类型:允许任何非 null/undefined 的值
let x: {};
x = 1;
x = "str";
x = {};
// x = null; // Error
63. TypeScript 元组
定义: 元组是已知固定长度和各元素类型的数组。
typescript
// 基本元组
let tuple: [string, number] = ["hello", 42];
// 命名元组元素(TS 4.0+)
type Response = [code: number, message: string];
let res: Response = [200, "OK"];
// 可选元素
let optionalTuple: [string, number?];
optionalTuple = ["hello"];
// 剩余元素
let restTuple: [string, ...number[]];
restTuple = ["hello", 1, 2, 3];
// 只读元组
let readonlyTuple: readonly [string, number] = ["hi", 1];
// readonlyTuple[0] = "bye"; // Error
64. 函数重载
定义: 函数重载允许同一个函数有多个签名定义,根据参数不同调用不同实现。
typescript
// 重载签名
function add(a: string, b: string): string;
function add(a: number, b: number): number;
// 实现签名
function add(a: string | number, b: string | number) {
if (typeof a === "string" && typeof b === "string") {
return a + b;
}
return (a as number) + (b as number);
}
add("a", "b"); // string
add(1, 2); // number
// add("a", 1); // Error: 没有匹配的重载
65. 可调用类型与可构造类型
typescript
// 可调用类型
interface AddFn {
(a: number, b: number): number;
}
const add: AddFn = (a, b) => a + b;
// 箭头函数类型
type MultiplyFn = (a: number, b: number) => number;
// 可构造类型
interface ClockConstructor {
new (hour: number, minute: number): ClockInterface;
}
interface ClockInterface {
tick(): void;
}
66. 映射类型的定义方式
定义: 映射类型基于已有类型创建新类型,通过遍历属性键来变换类型。
typescript
// 基本映射
type MyReadonly<T> = { readonly [P in keyof T]: T[P] };
type MyOptional<T> = { [P in keyof T]?: T[P] };
// 带 as 子句的映射(TS 4.1+)
type Getters<T> = {
[P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
};
interface Person { name: string; age: number; }
type LazyPerson = Getters<Person>;
// { getName(): string; getAge(): number }
// 过滤属性
type RemoveKindField<T> = {
[P in keyof T as Exclude<P, "kind">]: T[P];
};
67. 条件类型的语法和应用
定义: 条件类型根据类型关系选择不同类型的表达式,语法类似三元运算符。
typescript
// 基本语法: T extends U ? X : Y
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// 分布式条件类型(联合类型分发)
type ToArray<T> = T extends any ? T[] : never;
type A1 = ToArray<string | number>; // string[] | number[]
// 嵌套条件类型
type Flatten<T> = T extends Array<infer U> ? U : T;
68. infer 关键字的使用
定义: infer 用于在条件类型中推断类型变量,通常用于提取函数参数、返回值等。
typescript
// 提取返回值类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
// 提取参数类型
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
// 提取 Promise 值类型
type UnpackPromise<T> = T extends Promise<infer U> ? U : T;
type A = UnpackPromise<Promise<string>>; // string
// 提取数组元素类型
type ElementType<T> = T extends (infer U)[] ? U : T;
type B = ElementType<number[]>; // number
// 提取构造函数类型
type ConstructorParameters<T> = T extends new (...args: infer P) => any ? P : never;
69. 递归类型别名
typescript
// 递归深度只读
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
// 递归去除 Promise
type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;
// 递归路径类型
type Path<T> = T extends object
? {
[K in keyof T]: K extends string
? K | `${K}.${Path<T[K]>}`
: never;
}[keyof T]
: never;
70. 模板字面量类型
typescript
// 基本使用
type World = "world";
type Greeting = `hello ${World}`; // "hello world"
// 联合类型展开
type EventName = "click" | "hover" | "focus";
type EventMethod = `on${Capitalize<EventName>}Change`;
// "onClickChange" | "onHoverChange" | "onFocusChange"
// 类型推断
type GetProp<T, K extends string> = K extends `${infer First}.${infer Rest}`
? First extends keyof T
? GetProp<T[First], Rest>
: never
: K extends keyof T
? T[K]
: never;
71. 索引类型(索引访问类型)
typescript
interface Person {
name: string;
age: number;
location: { city: string; state: string };
}
type NameType = Person["name"]; // string
type LocationType = Person["location"]; // { city: string; state: string }
type CityType = Person["location"]["city"]; // string
// 联合索引
type NameOrAge = Person["name" | "age"]; // string | number
// keyof 操作符
type PersonKeys = keyof Person; // "name" | "age" | "location"
72. 类型体操 / 类型挑战
定义: 类型体操是利用 TypeScript 高级类型特性进行复杂类型变换的编程技巧。
typescript
// DeepPartial
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
// TupleToUnion
type TupleToUnion<T extends any[]> = T[number];
type A = TupleToUnion<[1, 2, 3]>; // 1 | 2 | 3
// LastElement
type Last<T extends any[]> = T extends [...any[], infer L] ? L : never;
type B = Last<[1, 2, 3]>; // 3
// Promise.all 返回值类型
type PromiseAll<T extends any[]> = Promise<{
[K in keyof T]: T[K] extends Promise<infer R> ? R : T[K];
}>;
十一、TypeScript 与 JavaScript 的对比
73. TypeScript 与 JavaScript 的区别
| 维度 | JavaScript | TypeScript |
|---|---|---|
| 类型系统 | 动态类型 | 静态类型(编译时) |
| 文件扩展名 | .js | .ts / .tsx |
| 运行方式 | 直接运行 | 需编译为 JS |
| 类型检查 | 运行时 | 编译时 |
| 可选参数 | 需要判断 | 原生支持 |
| 接口支持 | 无 | 有 |
| 泛型支持 | 无 | 有 |
| 枚举支持 | 无 | 有 |
| 装饰器 | 无(提案中) | 实验性支持 |
| 学习曲线 | 低 | 中等 |
| 开发效率 | 小项目高 | 大项目高 |
74. TypeScript 的优缺点有哪些?
优点:
- 静态类型检查: 编译时发现类型错误,减少运行时 bug
- 更好的 IDE 支持: 智能补全、跳转定义、重构
- 自文档化: 类型即文档,提高代码可读性
- 大规模项目友好: 类型约束使重构更安全
- 渐进式采用: 可以逐步从 JS 迁移
- 丰富的工具类型: 内置多种类型变换工具
- 社区生态: 主流框架和库都有 TS 支持
- 向后兼容: 兼容所有 JavaScript 特性
缺点:
- 学习曲线: 需要学习类型系统、泛型等概念
- 编译时间: 大型项目编译较慢
- 配置复杂度: tsconfig.json 配置项繁多
- 第三方库兼容: 部分库缺少类型声明
- 假安全感: 类型正确不等于逻辑正确
- 类型声明维护: 需要额外维护 .d.ts 文件
十二、TypeScript 生态与集成
75. 如何集成 TypeScript?
步骤:
bash
# 1. 安装 TypeScript
npm install -D typescript
# 2. 初始化配置
npx tsc --init
# 3. 安装类型声明
npm install -D @types/node
npm install -D @types/express
# 4. 编译
npx tsc
与构建工具集成:
javascript
// Webpack + ts-loader
{
module: {
rules: [
{ test: /\.tsx?$/, use: 'ts-loader', exclude: /node_modules/ }
]
},
resolve: { extensions: ['.tsx', '.ts', '.js'] }
}
// Vite(内置 TS 支持)
import { defineConfig } from 'vite';
export default defineConfig({ plugins: [] });
// Babel
// babel.config.js
module.exports = {
presets: [
["@babel/preset-typescript", { isTSX: true, allExtensions: true }]
]
};
76. TypeScript 与 React
typescript
// 函数组件
interface Props {
name: string;
age?: number;
onClick: (id: string) => void;
}
const UserCard: React.FC<Props> = ({ name, age = 0, onClick }) => {
return <div onClick={() => onClick("user-id")}>{name}</div>;
};
// Hooks 类型
const [count, setCount] = useState<number>(0);
const [user, setUser] = useState<User | null>(null);
const inputRef = useRef<HTMLInputElement>(null);
// 泛型组件
interface ListProps<T> {
items: T[];
renderItem: (item: T) => React.ReactNode;
}
function List<T>({ items, renderItem }: ListProps<T>) {
return <ul>{items.map(renderItem)}</ul>;
}
// 事件类型
const handleClick = (e: React.MouseEvent<HTMLButtonElement>) => {
console.log(e.currentTarget);
};
77. TypeScript 与 Vue
typescript
// Vue 3 Composition API
import { ref, computed, defineComponent } from "vue";
interface User { id: number; name: string; }
const user = ref<User | null>(null);
const count = ref(0);
const doubleCount = computed<number>(() => count.value * 2);
// defineComponent
export default defineComponent({
props: {
name: { type: String, required: true },
age: { type: Number }
},
setup(props) {
return { count, doubleCount };
}
});
// 自定义 Hook
function useCounter(initial = 0) {
const count = ref(initial);
const increment = () => count.value++;
return { count, increment };
}
78. TypeScript 与 Node.js
typescript
import express, { Request, Response, NextFunction } from "express";
interface UserRequest extends Request { userId?: string; }
const app = express();
app.get("/users/:id", (req: Request, res: Response) => {
const id = parseInt(req.params.id);
res.json({ id, name: "Alice" });
});
function auth(req: UserRequest, res: Response, next: NextFunction) {
req.userId = "123";
next();
}
tsconfig for Node:
json
{
"compilerOptions": {
"target": "ES2020",
"module": "CommonJS",
"outDir": "./dist",
"rootDir": "./src",
"strict": true,
"esModuleInterop": true
}
}
79. TypeScript 与 ESLint / Jest
javascript
// .eslintrc.js
module.exports = {
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
rules: {
"@typescript-eslint/no-explicit-any": "warn",
"@typescript-eslint/no-unused-vars": ["error", { "argsIgnorePattern": "^_" }]
}
};
// jest.config.js
module.exports = {
preset: "ts-jest",
testEnvironment: "node",
moduleFileExtensions: ["ts", "tsx", "js", "json"]
};
80. TypeScript 与 Webpack / Vite
javascript
// webpack.config.js
module.exports = {
entry: "./src/index.ts",
module: {
rules: [
{
test: /\.tsx?$/,
use: [{ loader: "ts-loader", options: { transpileOnly: true } }],
exclude: /node_modules/
}
]
},
resolve: { extensions: [".ts", ".tsx", ".js"] },
output: { filename: "bundle.js", path: path.resolve(__dirname, "dist") }
};
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
export default defineConfig({ plugins: [react()] });
十三、类型声明
81. TypeScript 类型声明
定义: 类型声明用于描述代码中的类型信息,包括 .d.ts 声明文件和内联类型注解。
typescript
// 内联类型声明
function greet(name: string): string {
return `Hello, ${name}`;
}
// 声明文件
declare module "my-lib" {
export function doSomething(): string;
}
82. 第三方库类型声明
bash
# 安装类型包
npm install -D @types/lodash
npm install -D @types/react
手动声明:
typescript
// types/my-lib.d.ts
declare module "my-lib" {
export function doSomething(): string;
export const VERSION: string;
}
83. 全局类型声明
typescript
// types/global.d.ts
declare global {
interface Window {
myCustomProperty: string;
}
}
export {};
84. 类型声明最佳实践
- 优先使用 DefinitelyTyped :
@types/*包 - 模块声明放 types 目录 :
declare module - 全局声明确保 export {}:避免污染全局
- 使用
import type:仅类型导入,编译后移除 - 避免声明文件中的
any:使用unknown替代
十四、项目实战
85. TypeScript 项目结构
bash
project/
├── src/
│ ├── types/ # 全局类型声明
│ │ └── index.d.ts
│ ├── interfaces/ # 接口定义
│ ├── models/ # 数据模型
│ ├── services/ # 业务逻辑
│ ├── components/ # UI 组件
│ ├── utils/ # 工具函数
│ └── index.ts # 入口文件
├── tsconfig.json # TS 配置
├── package.json
└── .eslintrc.js
86. TypeScript 最佳实践
typescript
// 1. 启用严格模式
// tsconfig: { "strict": true }
// 2. 避免使用 any,改用 unknown
function process(data: unknown) {
if (typeof data === "object" && data !== null) { /* ... */ }
}
// 3. 使用 as const 不变断言
const routes = [
{ path: "/", name: "home" },
{ path: "/about", name: "about" }
] as const;
// 4. 使用 satisfies 运算符(TS 4.9+)
const config = { host: "localhost", port: 3000 } satisfies Record<string, string | number>;
// 5. 优先使用 interface 定义对象
interface User { id: string; name: string; }
// 6. 使用类型守卫进行类型窄化
function isUser(data: unknown): data is User {
return typeof data === "object" && data !== null && "id" in data;
}
// 7. 合理使用泛型约束
function merge<T extends object, U extends object>(a: T, b: U): T & U {
return { ...a, ...b };
}
// 8. 使用类型导入优化编译
import type { User } from "./types";
87. JavaScript 项目迁移到 TypeScript
渐进式迁移步骤:
-
添加 TypeScript 依赖
bashnpm install -D typescript @types/node -
创建宽松配置
json{ "compilerOptions": { "allowJs": true, "checkJs": false, "strict": false, "outDir": "./dist" } } -
逐步重命名 .js 为 .ts
- 从工具函数和纯逻辑文件开始
- 添加类型注解
- 逐步启用 strict 模式
-
安装第三方库类型声明
bashnpm install -D @types/lodash @types/express -
最终目标配置
json{ "compilerOptions": { "strict": true, "noImplicitAny": true, "allowJs": false } }
88. TypeScript 编译原理
编译阶段:
- 词法分析: 将源代码分解为 Token 流
- 语法分析: 构建抽象语法树(AST)
- 语义分析: 类型检查和绑定
- 代码生成: 输出目标 JavaScript
AST 结构示例:
scss
SourceFile
└── FunctionDeclaration
├── Identifier (name)
├── Parameter (type)
└── Block
└── ReturnStatement
89. TypeScript 调试与源码映射
json
{
"compilerOptions": {
"sourceMap": true
}
}
VS Code 调试配置:
json
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Debug TS",
"program": "${workspaceFolder}/src/index.ts",
"preLaunchTask": "tsc: build",
"sourceMaps": true
}
]
}
90. TypeScript 常见错误
| 错误码 | 原因 | 解决方案 |
|---|---|---|
TS2322 |
类型不匹配 | 检查赋值类型 |
TS2345 |
参数类型不兼容 | 检查函数签名 |
TS2339 |
属性不存在 | 检查类型定义或使用索引访问 |
TS2304 |
找不到名称 | 导入或声明类型 |
TS7006 |
参数隐式 any | 添加类型注解 |
TS2532 |
对象可能是 undefined | 使用可选链或类型守卫 |
91. TypeScript 性能优化
- 使用
skipLibCheck: true跳过声明文件检查 - 使用
incremental: true启用增量编译 - 使用
ts-loader的transpileOnly模式 - 合理配置
include/exclude - 使用
// @ts-nocheck跳过特定文件 - 使用项目引用(Project References) 拆分大项目
- 避免过深的类型嵌套
- 减少
any使用 提高类型推断效率
json
{
"compilerOptions": {
"skipLibCheck": true,
"incremental": true,
"tsBuildInfoFile": "./node_modules/.cache/tsbuildinfo.json"
}
}