前言
TypeScript 是 JavaScript 的超集1TypeScript 是 JavaScript 的超集,意味着所有有效的 JavaScript 代码都是有效的 TypeScript 代码。TypeScript 添加了类型注解和编译时检查,最终编译为纯 JavaScript。,增加了静态类型系统2类型系统是编程语言用来检查和约束值类型的规则集合。静态类型系统(如 TypeScript)在编译时检查,动态类型系统(如 JavaScript)在运行时检查。。它不能让你少写代码,但能让你在编译时而不是运行时发现 bug,配合编辑器提示体验大幅提升。
如果你写过几万行 JS 然后被 undefined is not a function 折磨过,你会理解 TypeScript 的价值。
基础类型
// 基本类型let name: string = 'Alice'; // 类型推断[^type-inference]可省略: let name = 'Alice'let age: number = 30;let isActive: boolean = true;let data: null = null;let undef: undefined = undefined;
// 数组let list: number[] = [1, 2, 3];let list2: Array<number> = [1, 2, 3]; // 泛型写法
// 元组(固定长度和类型)let tuple: [string, number] = ['Alice', 30];
// 枚举enum Direction { Up, Down, Left, Right }let dir: Direction = Direction.Up;
// any(尽量少用)let loose: any = 'anything';loose = 42;| 类型 | 说明 | 何时使用 |
|---|---|---|
any | 关闭类型检查 | |
unknown | 类型安全版 any | 不确定类型时用,使用前需判断 |
void | 无返回值 | 函数不返回任何值 |
never | 永远不会返回 | 抛出异常或无限循环的函数 |
// unknown 需要先判断再使用function process(value: unknown) { if (typeof value === 'string') { console.log(value.toUpperCase()); }}接口与类型别名
Interface vs Type
| 对比 | interface | type |
|---|---|---|
| 扩展(继承) | extends | & intersection |
| 合并声明 | ✅ 同名自动合并 | ❌ 不可重复声明 |
| 联合类型 | ❌ | ✅ |
| 工具类型(映射等) | ❌ | ✅ |
| 实现 | implements | implements |
// Interface(推荐用于对象定义)interface User { id: number; name: string; email: string; age?: number; // 可选属性 readonly createdAt: Date; // 只读}
interface Admin extends User { role: 'admin';}
// Type(适合联合类型、工具类型)type Status = 'active' | 'inactive' | 'pending';type Point = { x: number; y: number };type NamedPoint = Point & { name: string };优先用 interface 定义对象结构,用 type 定义联合类型、交叉类型、工具类型。这是 TypeScript 官方的推荐风格。
可选与只读
interface Config { url: string; timeout?: number; // 可选:可以不传 readonly apiKey: string; // 只读:初始化后不可修改}
const cfg: Config = { url: 'https://api.example.com', apiKey: 'sk-xxx' };cfg.apiKey = 'new-key'; // ❌ 报错:只读泛型(Generics)3泛型(Generics)允许在定义函数、接口或类时使用类型参数,在调用时再指定具体类型。它是类型系统的参数化多态机制,提高代码复用性。
泛型让类型像参数一样传递,是 TypeScript 复用能力的关键。
基础用法
// 泛型函数function firstElement<T>(arr: T[]): T | undefined { return arr[0];}
const num = firstElement([1, 2, 3]); // number | undefinedconst str = firstElement(['a', 'b']); // string | undefined
// 泛型接口interface ApiResponse<T> { code: number; data: T; message: string;}
type UserResponse = ApiResponse<User>;type ListResponse = ApiResponse<User[]>;
// 泛型约束function getLength<T extends { length: number }>(arg: T): number { return arg.length;}
getLength('hello'); // ✅ string 有 lengthgetLength([1, 2, 3]); // ✅ array 有 lengthgetLength(42); // ❌ number 没有 length常用泛型工具
// Partial:所有属性变可选type PartialUser = Partial<User>;
// Required:所有属性变必选type RequiredUser = Required<User>;
// Pick:选取部分属性type UserName = Pick<User, 'id' | 'name'>;
// Omit:排除部分属性type UserWithoutEmail = Omit<User, 'email'>;
// Record:构造键值对类型type PageMap = Record<string, number>;
// Exclude / Extract:联合类型过滤type T = Exclude<'a' | 'b' | 'c', 'a'>; // 'b' | 'c'type T2 = Extract<'a' | 'b' | 'c', 'a'>; // 'a'类型守卫与类型窄化4类型窄化(Type Narrowing)是 TypeScript 根据控制流分析缩小变量类型范围的机制。通过 typeof、instanceof、in 等判断,将联合类型逐步缩窄为具体类型。
function print(value: string | number | null) { if (value === null) { console.log('null'); } else if (typeof value === 'string') { console.log(value.toUpperCase()); // 窄化为 string } else { console.log(value.toFixed(2)); // 窄化为 number }}| 窄化方式 | 说明 |
|---|---|
typeof | 基本类型判断 |
instanceof | 类实例判断 |
in | 属性存在判断 |
=== null / === undefined | null/undefined 判断 |
| 用户自定义守卫 | value is Type 返回值 |
// 自定义类型守卫interface Cat { meow: () => void }interface Dog { bark: () => void }
function isCat(pet: Cat | Dog): pet is Cat { return (pet as Cat).meow !== undefined;}
function handle(pet: Cat | Dog) { if (isCat(pet)) { pet.meow(); // ✅ 窄化为 Cat }}类型断言
当你比 TypeScript 更清楚一个值的类型时使用:
// as 断言const input = document.getElementById('input') as HTMLInputElement;input.value = 'hello';
// 非空断言(谨慎使用)const name = maybeNull!.name;
// 双重断言(不推荐)const num = (value as unknown) as number;类型断言是”告诉编译器你知道自己在做什么”,不会在运行时做任何检查。滥用 。as any 等于放弃了类型检查的收益
函数
// 函数参数和返回值function add(a: number, b: number): number { return a + b;}
// 可选参数function greet(name: string, greeting?: string): string { return `${greeting ?? 'Hello'}, ${name}!`;}
// 默认参数function createUrl(path: string, base: string = 'https://example.com'): string { return `${base}${path}`;}
// 剩余参数function sum(...numbers: number[]): number { return numbers.reduce((a, b) => a + b, 0);}
// 函数重载function process(value: string): string[];function process(value: number): number[];function process(value: string | number): string[] | number[] { if (typeof value === 'string') { return value.split(''); } return [value];}class
class Animal { // 可见性修饰符 public name: string; // 公开(默认) private age: number; // 私有(仅本类) protected type: string; // 受保护(本类及子类)
// 构造函数的参数自动变成属性 constructor(public id: number, name: string) { this.name = name; this.age = 0; this.type = 'unknown'; }
// 方法 public speak(): void { console.log(`${this.name} makes a sound`); }
// getter / setter get description(): string { return `${this.name} (${this.age}岁)`; }
// 静态属性 static kingdom = 'Animalia';}实用工具类型5工具类型是 TypeScript 内置的泛型类型,用于常见类型转换。如 Partial 将所有属性变可选,Pick<T, K> 选取部分属性,Omit<T, K> 排除部分属性。
TypeScript 内置了一系列工具类型,极大简化类型操作:
// 属性修饰变化interface Todo { title: string; description: string; completed: boolean;}
type PartialTodo = Partial<Todo>; // 全可选type RequiredTodo = Required<Todo>; // 全必选type ReadonlyTodo = Readonly<Todo>; // 全只读
// 属性选取type Picked = Pick<Todo, 'title' | 'completed'>;type Omitted = Omit<Todo, 'description'>;
// 提取和排除联合类型成员type T1 = Exclude<'a' | 'b' | 'c', 'a'>; // 'b' | 'c'type T2 = Extract<'a' | 'b' | 'c', 'a' | 'b'>; // 'a' | 'b'
// 获取函数返回值类型type Result = ReturnType<typeof fetch>; // Promise<Response>
// 获取函数参数类型type Params = Parameters<(a: string, b: number) => void>;// [string, number]
// 获取 Promise 内部类型type Data = Awaited<Promise<string>>; // string
// 构造键值对type PageNames = Record<'home' | 'about' | 'contact', string>;声明文件(.d.ts)6声明文件(.d.ts)描述 JavaScript 代码的类型信息,不包含实现。用于为纯 JS 库提供类型支持,或声明全局变量、模块类型。
// globals.d.ts — 声明全局变量declare const VERSION: string;
// modules.d.ts — 声明模块类型declare module '*.svg' { const content: string; export default content;}
declare module 'some-library' { export function hello(): void; export const version: string;}实际应用模式
类型安全的 API 请求
interface ApiResponse<T> { code: number; data: T; message: string;}
async function fetchApi<T>(url: string): Promise<T> { const res = await fetch(url); const json: ApiResponse<T> = await res.json(); if (json.code !== 0) throw new Error(json.message); return json.data;}
// 使用interface User { id: number; name: string }const user = await fetchApi<User>('/api/user/1');// user 的类型是 User事件处理器映射
type EventMap = { click: { x: number; y: number }; keydown: { key: string }; focus: void;};
function onEvent<E extends keyof EventMap>( event: E, handler: (data: EventMap[E]) => void) { // ...}
onEvent('click', (data) => { console.log(data.x, data.y); // ✅ 类型正确});
onEvent('keydown', (data) => { console.log(data.key); // ✅ 类型正确});条件类型
// 根据条件返回不同的类型type IsString<T> = T extends string ? 'yes' : 'no';
type A = IsString<string>; // 'yes'type B = IsString<number>; // 'no'
// infer:提取类型type Unpack<T> = T extends (infer U)[] ? U : T;
type C = Unpack<string[]>; // stringtype D = Unpack<number>; // numberconst 断言
// 让字面量推导为确切值而非通用类型const COLORS = { primary: '#007bff', secondary: '#6c757d',} as const;
// 相当于:// { readonly primary: '#007bff'; readonly secondary: '#6c757d' }
// 数组 const 断言const roles = ['admin', 'user', 'guest'] as const;type Role = (typeof roles)[number]; // 'admin' | 'user' | 'guest'TypeScript 配置
{ "compilerOptions": { "target": "ES2020", // 编译目标版本 "module": "ESNext", // 模块格式 "moduleResolution": "bundler", // 模块解析策略 "strict": true, // 启用全部严格检查(推荐) "esModuleInterop": true, // 兼容 CommonJS 模块 "skipLibCheck": true, // 跳过 .d.ts 检查(加速) "forceConsistentCasingInFileNames": true, "resolveJsonModule": true, // 允许 import JSON "declaration": true, // 生成 .d.ts 文件 "outDir": "./dist", "rootDir": "./src" }, "include": ["src"], "exclude": ["node_modules", "dist"]}| 严格模式配置 | 作用 |
|---|---|
strict: true | 开启下面所有严格检查 |
noImplicitAny | 不允许隐式 any |
strictNullChecks | null/undefined 不能赋值给其他类型 |
strictFunctionTypes | 函数类型参数逆变检查 |
strictBindCallApply | bind/call/apply 类型检查 |
noUnusedLocals | 不允许未使用的局部变量 |
noUnusedParameters | 不允许未使用的参数 |
学习路线图
- 掌握基础类型(string/number/boolean/array/tuple/enum)
- 理解 interface 和 type 的区别和用法
- 学会泛型的基本使用
- 掌握类型窄化和守卫
- 熟练使用内置工具类型
- 理解条件类型和 infer
- 能写简单的声明文件
- 了解类型体操(高级泛型编程)
总结
| 知识点 | 要点 |
|---|---|
| 基础类型 | string/number/boolean/array/tuple/enum/unknown/never |
| 接口与类型 | interface 定义对象,type 定义联合/交叉 |
| 泛型 | <T> 让类型可参数化,约束用 extends |
| 窄化 | typeof / instanceof / in / 自定义守卫 |
| 工具类型 | Partial/Pick/Omit/Record/ReturnType/Awaited |
| 声明文件 | .d.ts 描述 JS 库的类型 |
| 配置 | strict: true 是必选项 |
| 类型系统 | 结构化类型系统7TypeScript 使用结构化类型系统(Structural Typing),也称鸭子类型。只要两个类型的结构兼容(属性和方法匹配),就可以互相赋值,不需要显式声明继承关系。(鸭子类型) |
TypeScript 的学习曲线比 JavaScript 陡一些,但一旦习惯了类型提示就回不去了。从 strict: true 开始,配合编辑器的自动补全,适应期不会太长。
参考来源
TypeScript 是 JavaScript 的超集,意味着所有有效的 JavaScript 代码都是有效的 TypeScript 代码。TypeScript 添加了类型注解和编译时检查,最终编译为纯 JavaScript。
↩类型系统是编程语言用来检查和约束值类型的规则集合。静态类型系统(如 TypeScript)在编译时检查,动态类型系统(如 JavaScript)在运行时检查。
↩泛型(Generics)允许在定义函数、接口或类时使用类型参数,在调用时再指定具体类型。它是类型系统的参数化多态机制,提高代码复用性。
↩类型窄化(Type Narrowing)是 TypeScript 根据控制流分析缩小变量类型范围的机制。通过 typeof、instanceof、in 等判断,将联合类型逐步缩窄为具体类型。
↩工具类型是 TypeScript 内置的泛型类型,用于常见类型转换。如 Partial 将所有属性变可选,Pick<T, K> 选取部分属性,Omit<T, K> 排除部分属性。
↩声明文件(.d.ts)描述 JavaScript 代码的类型信息,不包含实现。用于为纯 JS 库提供类型支持,或声明全局变量、模块类型。
↩TypeScript 使用结构化类型系统(Structural Typing),也称鸭子类型。只要两个类型的结构兼容(属性和方法匹配),就可以互相赋值,不需要显式声明继承关系。
↩
评论
GitHub 登录后可评论。
评论区会在滚动到这里时自动加载。