TypeScript 进阶知识总结:从 extends、泛型到 infer,一篇打通 TS 类型系统
前言
TypeScript 的核心价值不是“给 JavaScript 加几个类型注释”,而是用类型系统提前描述数据结构、函数契约和业务状态。
这篇文章会从基础类型讲起,重点展开几个 TS 进阶高频点:
- 索引签名
[key: string]: unknown -
in类型收窄 -
extends的多种用法 -
value is Xxx类型守卫 -
object、Object、unknown、any的区别 - 联合类型和交叉类型
- 函数重载
- 泛型
infer- 常用工具类型源码实现和使用场景
1. TypeScript 到底解决什么问题?
TypeScript = JavaScript + 静态类型检查。
它最终还是会编译成 JavaScript,类型只存在于编译期。
function add(a: number, b: number): number {
return a + b;
}
add(1, 2);
add("1", 2); // 报错
TS 的作用是:在代码运行前,提前发现类型错误。
2. 基础类型
常见基础类型:
let name: string = "Tom";
let age: number = 18;
let isAdmin: boolean = false;
let n: null = null;
let u: undefined = undefined;
let big: bigint = 100n;
let sym: symbol = Symbol("id");
数组:
const nums: number[] = [1, 2, 3];
const names: Array<string> = ["Tom", "Jerry"];
元组:
const user: [string, number] = ["Tom", 18];
对象:
const user: { id: string; name: string } = {
id: "001",
name: "Tom",
};
3. any、unknown、never、void
3.1 any
any 表示任意类型,但会关闭类型检查。
let value: any = "hello";
value.toFixed(); // TS 不报错,但运行可能报错
3.2 unknown
unknown 也可以接收任意类型,但使用前必须判断。
let value: unknown = "hello";
if (typeof value === "string") {
value.toUpperCase();
}
所以:
unknown = 类型安全版 any
3.3 void
void 常用于表示函数没有返回值。
function log(message: string): void {
console.log(message);
}
3.4 never
never 表示永远不会出现的值。
function fail(message: string): never {
throw new Error(message);
}
也常用于穷尽检查:
function assertNever(value: never): never {
throw new Error(`Unexpected value: ${value}`);
}
4. 索引签名:[key: string]: any
你可能见过这样的写法:
type Obj = {
[props: string]: any;
};
它表示:
这个对象可以有任意字符串 key,并且 value 是 any 类型。
props 只是名字,可以换成 key:
type Obj = {
[key: string]: any;
};
但是项目里更推荐:
type Obj = {
[key: string]: unknown;
};
因为 any 会放弃类型检查,而 unknown 更安全。
const obj: Obj = {
name: "Tom",
age: 18,
};
const name = obj.name;
if (typeof name === "string") {
name.toUpperCase();
}
注意,一旦写了索引签名,明确属性也要兼容它:
type User = {
name: string;
age: number;
[key: string]: string | number;
};
5. in 的三种用法
5.1 判断属性是否存在
const user = {
name: "Tom",
age: 18,
};
console.log("name" in user); // true
console.log("email" in user); // false
5.2 联合类型收窄
type Cat = {
meow: () => void;
};
type Dog = {
bark: () => void;
};
function speak(animal: Cat | Dog) {
if ("meow" in animal) {
animal.meow();
} else {
animal.bark();
}
}
"meow" in animal 会让 TS 知道当前分支里 animal 是 Cat。
5.3 映射类型中遍历 key
type MyPartial<T> = {
[K in keyof T]?: T[K];
};
这里的 in 表示遍历 keyof T 中的每一个 key。
6. extends:不只是继承
extends 是 TS 中非常重要的关键字。核心可以理解成:
A extends B
意思是:
A 必须满足 B。
6.1 interface 继承
interface Person {
name: string;
}
interface Student extends Person {
school: string;
}
Student 同时拥有 name 和 school。
const s: Student = {
name: "Tom",
school: "No.1 School",
};
6.2 class 继承
class Animal {
move() {
console.log("move");
}
}
class Dog extends Animal {
bark() {
console.log("bark");
}
}
6.3 泛型约束
function printName<T extends { name: string }>(obj: T) {
console.log(obj.name);
}
意思是:
T 必须至少有
name: string。
printName({ name: "Tom", age: 18 }); // OK
printName({ age: 18 }); // 报错
你也可以写得更具体:
function handle<T extends { id: string; name: string }>(item: T): T {
return item;
}
这表示 T 至少要有 id 和 name:
const result = handle({
id: "001",
name: "Tom",
age: 18,
});
result.age; // OK
泛型约束的价值是:
既限制结构,又保留传入对象的完整类型。
6.4 条件类型
type IsString<T> = T extends string ? true : false;
使用:
type A = IsString<string>; // true
type B = IsString<number>; // false
这里的 extends 更准确地说是:
T 是否可以赋值给 string。
例如:
type A = "hello" extends string ? true : false;
// true
type B = string extends "hello" ? true : false;
// false
7. T extends object
function fn<T extends object>(value: T) {
return value;
}
T extends object 表示 T 必须是非原始类型。
可以:
fn({});
fn([]);
fn(() => {});
fn(new Date());
不可以:
fn("hello");
fn(123);
fn(true);
注意:数组和函数也属于 object。
如果你想表达普通键值对象,可以考虑:
Record<string, unknown>
但它和普通对象也不是完全等价,实际项目里要看具体场景。
8. object 和 Object 的区别
8.1 object
object 表示非原始类型。
let a: object;
a = {};
a = [];
a = () => {};
a = "hello"; // 报错
a = 123; // 报错
8.2 Object
Object 范围更宽,几乎表示所有非 null、非 undefined 的值。
let b: Object;
b = {};
b = [];
b = "hello";
b = 123;
b = true;
所以真实项目里不建议用大写 Object 表示普通对象。
更推荐:
unknown // 任意类型,安全
object // 非原始类型
Record<string, unknown> // 普通键值对象
9. 不用 any / unknown,如何表示所有 JS 值?
可以用联合类型:
type Primitive =
| string
| number
| boolean
| bigint
| symbol
| null
| undefined;
type AnyValue = Primitive | object;
因为 object 包含普通对象、数组、函数、Date、Map、Set 等非原始值。
如果你要表示 JSON 值,可以这样写:
type JsonValue =
| string
| number
| boolean
| null
| JsonValue[]
| { [key: string]: JsonValue };
JSON 不包含:
undefined
function
symbol
bigint
Date
Map
Set
10. 类型守卫:value is Xxx
类型守卫用于告诉 TS:
如果这个函数返回 true,那么参数就是某个类型。
function isPlainObject(value: unknown): value is Record<string, unknown> {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
使用:
function handle(value: unknown) {
if (isPlainObject(value)) {
value.name; // OK,类型是 unknown
}
}
属性值仍然是 unknown,所以要继续判断:
function handle(value: unknown) {
if (isPlainObject(value) && typeof value.name === "string") {
value.name.toUpperCase();
}
}
类型守卫不必须和 unknown 配合,也可以用于联合类型:
function isString(value: string | number): value is string {
return typeof value === "string";
}
11. 联合类型和交叉类型
11.1 联合类型 |
联合类型表示“或”。
type ID = string | number;
使用时需要收窄:
function printId(id: string | number) {
if (typeof id === "string") {
id.toUpperCase();
} else {
id.toFixed(2);
}
}
适合表达多种可能:
type Status = "loading" | "success" | "error";
11.2 交叉类型 &
交叉类型表示“且”。
type User = { name: string } & { age: number };
const user: User = {
name: "Tom",
age: 18,
};
交叉类型常用于合并对象结构:
type Base = {
id: string;
createdAt: string;
};
type User = Base & {
name: string;
};
注意基础类型交叉通常会得到 never:
type A = string & number;
// never
对象属性冲突也会导致不可能类型:
type A = { id: string } & { id: number };
// id: never
12. 函数重载
函数重载用于表达:
同一个函数,不同参数类型或参数数量,对应不同返回类型。
function fn(x: string): string;
function fn(x: number): number;
function fn(x: string | number): string | number {
return x;
}
前两行是重载签名,最后一行是实现签名。
调用时:
const a = fn("hello"); // string
const b = fn(123); // number
参数数量不同也可以重载:
function makeDate(timestamp: number): Date;
function makeDate(year: number, month: number, day: number): Date;
function makeDate(
yearOrTimestamp: number,
month?: number,
day?: number
): Date {
if (month !== undefined && day !== undefined) {
return new Date(yearOrTimestamp, month, day);
}
return new Date(yearOrTimestamp);
}
不允许两个参数调用:
makeDate(2026, 6); // 报错
如果返回值不随参数类型变化,优先用联合类型。
如果输入和输出保持同一种类型关系,优先用泛型。
13. 泛型
泛型可以理解成:
把类型当成参数传进去。
function identity<T>(value: T): T {
return value;
}
调用:
const a = identity("hello"); // string
const b = identity(123); // number
泛型的价值是:
既能复用逻辑,又能保留具体类型。
13.1 数组例子
function first<T>(arr: T[]): T | undefined {
return arr[0];
}
const a = first([1, 2, 3]); // number | undefined
const b = first(["a", "b"]); // string | undefined
13.2 多个泛型参数
function pair<T, U>(a: T, b: U): [T, U] {
return [a, b];
}
const result = pair("age", 18);
// [string, number]
13.3 泛型接口
type ApiResponse<T> = {
code: number;
message: string;
data: T;
};
使用:
type User = {
id: string;
name: string;
};
const res: ApiResponse<User> = {
code: 0,
message: "ok",
data: {
id: "001",
name: "Tom",
},
};
13.4 泛型默认值
type ApiResponse<T = unknown> = {
code: number;
data: T;
};
14. 泛型和 keyof
这个模板非常重要:
function getValue<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
使用:
const user = {
id: 1,
name: "Tom",
};
const id = getValue(user, "id"); // number
const name = getValue(user, "name"); // string
getValue(user, "age"); // 报错
这里同时用到了:
T
K extends keyof T
T[K]
它表达的是:
key 必须是 obj 的 key,返回值类型就是这个 key 对应的 value 类型。
15. infer
infer 用于条件类型中,作用是:
从一个类型结构中提取某一部分类型。
15.1 提取数组元素类型
type Item<T> = T extends Array<infer U> ? U : never;
使用:
type A = Item<string[]>; // string
type B = Item<number[]>; // number
15.2 提取 Promise 结果
type UnwrapPromise<T> = T extends Promise<infer R> ? R : T;
type A = UnwrapPromise<Promise<string>>;
// string
递归拆 Promise:
type DeepUnwrapPromise<T> = T extends Promise<infer R>
? DeepUnwrapPromise<R>
: T;
15.3 提取函数返回值
type MyReturnType<T> =
T extends (...args: any[]) => infer R ? R : never;
使用:
function getUser() {
return {
id: 1,
name: "Tom",
};
}
type User = MyReturnType<typeof getUser>;
// { id: number; name: string }
15.4 提取函数参数
type MyParameters<T> =
T extends (...args: infer P) => any ? P : never;
function createUser(id: string, age: number) {}
type Params = MyParameters<typeof createUser>;
// [id: string, age: number]
15.5 提取对象字段
type GetData<T> = T extends { data: infer D } ? D : never;
type Response = {
code: number;
data: {
id: string;
name: string;
};
};
type Data = GetData<Response>;
// { id: string; name: string }
16. TS 里的常用工具类型:源码实现 + 使用场景
TypeScript 内置了很多工具类型,它们本质上都是基于这些能力组合出来的:
- 泛型
keyof- 映射类型
[K in keyof T] - 条件类型
T extends U ? X : Y infer- 联合类型分发
下面逐个看常用工具类型。
16.1 Partial<T>
Partial<T> 会把对象类型的所有属性变成可选。
简化源码:
type MyPartial<T> = {
[K in keyof T]?: T[K];
};
使用示例:
type User = {
id: string;
name: string;
age: number;
};
type PartialUser = Partial<User>;
等价于:
type PartialUser = {
id?: string;
name?: string;
age?: number;
};
使用场景:更新部分字段。
function updateUser(id: string, patch: Partial<User>) {
// 只更新传入的字段
}
updateUser("001", {
name: "Jerry",
});
16.2 Required<T>
Required<T> 会把对象类型的所有属性变成必选。
简化源码:
type MyRequired<T> = {
[K in keyof T]-?: T[K];
};
这里的 -? 表示移除可选标记。
使用示例:
type User = {
id?: string;
name?: string;
};
type RequiredUser = Required<User>;
等价于:
type RequiredUser = {
id: string;
name: string;
};
使用场景:配置合并后,内部使用完整配置。
type Config = {
host?: string;
port?: number;
};
const defaultConfig: Required<Config> = {
host: "localhost",
port: 3000,
};
function createServer(config: Config) {
const finalConfig: Required<Config> = {
...defaultConfig,
...config,
};
finalConfig.host;
finalConfig.port;
}
16.3 Readonly<T>
Readonly<T> 会把对象属性变成只读。
简化源码:
type MyReadonly<T> = {
readonly [K in keyof T]: T[K];
};
使用示例:
type User = {
id: string;
name: string;
};
type ReadonlyUser = Readonly<User>;
等价于:
type ReadonlyUser = {
readonly id: string;
readonly name: string;
};
使用场景:防止函数内部修改传入对象。
function printUser(user: Readonly<User>) {
console.log(user.name);
user.name = "Jerry"; // 报错
}
注意:Readonly<T> 默认是浅只读。
type User = {
profile: {
age: number;
};
};
const user: Readonly<User> = {
profile: {
age: 18,
},
};
user.profile.age = 20; // 可以,因为 profile 内部不是 readonly
16.4 Pick<T, K>
Pick<T, K> 从对象类型中挑选部分属性。
简化源码:
type MyPick<T, K extends keyof T> = {
[P in K]: T[P];
};
使用示例:
type User = {
id: string;
name: string;
age: number;
password: string;
};
type UserBaseInfo = Pick<User, "id" | "name">;
等价于:
type UserBaseInfo = {
id: string;
name: string;
};
使用场景:接口返回、组件 props、表格字段。
type UserCardProps = Pick<User, "id" | "name">;
function renderUserCard(user: UserCardProps) {
return `${user.id} - ${user.name}`;
}
16.5 Omit<T, K>
Omit<T, K> 从对象类型中排除部分属性。
简化源码:
type MyOmit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
拆开看:
Exclude<keyof T, K>
先从所有 key 里排除 K,再用 Pick 取剩下的字段。
使用示例:
type User = {
id: string;
name: string;
password: string;
};
type PublicUser = Omit<User, "password">;
等价于:
type PublicUser = {
id: string;
name: string;
};
使用场景:隐藏敏感字段。
function toPublicUser(user: User): Omit<User, "password"> {
const { password, ...rest } = user;
return rest;
}
也常用于创建参数:
type CreateUserDto = Omit<User, "id">;
16.6 Record<K, T>
Record<K, T> 用来创建一个 key/value 对象类型。
简化源码:
type MyRecord<K extends keyof any, T> = {
[P in K]: T;
};
keyof any 等价于:
string | number | symbol
使用示例:
type Role = "admin" | "user" | "guest";
type RoleMap = Record<Role, string>;
等价于:
type RoleMap = {
admin: string;
user: string;
guest: string;
};
使用场景:枚举映射、状态映射、字典对象。
type Status = "pending" | "success" | "error";
const statusText: Record<Status, string> = {
pending: "处理中",
success: "成功",
error: "失败",
};
这样如果少写一个 key,TS 会报错:
const statusText: Record<Status, string> = {
pending: "处理中",
success: "成功",
// error 缺失,报错
};
16.7 Exclude<T, U>
Exclude<T, U> 从联合类型 T 中排除可以赋值给 U 的类型。
简化源码:
type MyExclude<T, U> = T extends U ? never : T;
使用示例:
type Status = "pending" | "success" | "error";
type NonErrorStatus = Exclude<Status, "error">;
结果:
type NonErrorStatus = "pending" | "success";
使用场景:从联合类型里排除某些值。
type EventName = "click" | "hover" | "focus";
type MouseEventName = Exclude<EventName, "focus">;
// "click" | "hover"
理解重点:
type MyExclude<T, U> = T extends U ? never : T;
当 T 是联合类型时,条件类型会自动分发:
Exclude<"a" | "b" | "c", "a">
相当于:
("a" extends "a" ? never : "a")
| ("b" extends "a" ? never : "b")
| ("c" extends "a" ? never : "c")
结果:
"b" | "c"
16.8 Extract<T, U>
Extract<T, U> 从联合类型 T 中提取可以赋值给 U 的类型。
简化源码:
type MyExtract<T, U> = T extends U ? T : never;
使用示例:
type Status = "pending" | "success" | "error";
type SuccessStatus = Extract<Status, "success" | "done">;
结果:
type SuccessStatus = "success";
使用场景:从联合类型中取交集。
type FrontendEvent = "click" | "hover" | "focus";
type SupportedEvent = "click" | "focus";
type AvailableEvent = Extract<FrontendEvent, SupportedEvent>;
// "click" | "focus"
16.9 NonNullable<T>
NonNullable<T> 从类型中排除 null 和 undefined。
简化源码:
type MyNonNullable<T> = T extends null | undefined ? never : T;
使用示例:
type Value = string | number | null | undefined;
type SafeValue = NonNullable<Value>;
结果:
type SafeValue = string | number;
使用场景:处理已经判空后的类型。
function assertValue<T>(value: T): NonNullable<T> {
if (value === null || value === undefined) {
throw new Error("value is empty");
}
return value;
}
16.10 ReturnType<T>
ReturnType<T> 用来获取函数返回值类型。
简化源码:
type MyReturnType<T extends (...args: any[]) => any> =
T extends (...args: any[]) => infer R ? R : never;
使用示例:
function getUser() {
return {
id: "001",
name: "Tom",
};
}
type User = ReturnType<typeof getUser>;
结果:
type User = {
id: string;
name: string;
};
使用场景:复用函数返回值类型,避免重复声明。
function createState() {
return {
count: 0,
user: null as null | { id: string; name: string },
};
}
type State = ReturnType<typeof createState>;
16.11 Parameters<T>
Parameters<T> 用来获取函数参数类型,结果是一个元组。
简化源码:
type MyParameters<T extends (...args: any[]) => any> =
T extends (...args: infer P) => any ? P : never;
使用示例:
function createUser(id: string, age: number) {
return { id, age };
}
type CreateUserParams = Parameters<typeof createUser>;
结果:
type CreateUserParams = [id: string, age: number];
使用场景:封装函数、转发参数。
function createUser(id: string, age: number) {
return { id, age };
}
function wrapper(...args: Parameters<typeof createUser>) {
return createUser(...args);
}
16.12 ConstructorParameters<T>
ConstructorParameters<T> 用来获取构造函数参数类型。
简化源码:
type MyConstructorParameters<T extends abstract new (...args: any[]) => any> =
T extends abstract new (...args: infer P) => any ? P : never;
使用示例:
class User {
constructor(public id: string, public name: string) {}
}
type UserConstructorParams = ConstructorParameters<typeof User>;
结果:
type UserConstructorParams = [id: string, name: string];
使用场景:工厂函数。
function createInstance<T extends abstract new (...args: any[]) => any>(
Ctor: T,
...args: ConstructorParameters<T>
): InstanceType<T> {
return new Ctor(...args);
}
如果遇到 abstract new 不好理解,可以先记:
new (...args: any[]) => any
表示构造函数类型。
16.13 InstanceType<T>
InstanceType<T> 用来获取构造函数创建出来的实例类型。
简化源码:
type MyInstanceType<T extends abstract new (...args: any[]) => any> =
T extends abstract new (...args: any[]) => infer R ? R : never;
使用示例:
class User {
id = "001";
name = "Tom";
}
type UserInstance = InstanceType<typeof User>;
结果:
type UserInstance = User;
使用场景:根据 class 自动得到实例类型。
class Service {
request() {}
}
type ServiceInstance = InstanceType<typeof Service>;
const service: ServiceInstance = new Service();
16.14 Awaited<T>
Awaited<T> 用来获取 Promise 最终 resolve 出来的类型。
真实源码更复杂,这里写一个简化版本:
type MyAwaited<T> = T extends Promise<infer R>
? MyAwaited<R>
: T;
使用示例:
type A = Awaited<Promise<string>>;
// string
type B = Awaited<Promise<Promise<number>>>;
// number
使用场景:提取异步函数返回数据类型。
async function fetchUser() {
return {
id: "001",
name: "Tom",
};
}
type User = Awaited<ReturnType<typeof fetchUser>>;
拆开看:
ReturnType<typeof fetchUser>
得到:
Promise<{ id: string; name: string }>
再用:
Awaited<...>
得到:
{ id: string; name: string }
16.15 ReadonlyArray<T>
ReadonlyArray<T> 表示只读数组。
使用示例:
const nums: ReadonlyArray<number> = [1, 2, 3];
nums.push(4); // 报错
nums[0] = 10; // 报错
也可以写成:
const nums: readonly number[] = [1, 2, 3];
使用场景:函数不应该修改传入数组。
function sum(nums: readonly number[]) {
return nums.reduce((total, item) => total + item, 0);
}
16.16 ThisParameterType<T>
ThisParameterType<T> 用来提取函数里的 this 参数类型。
简化源码:
type MyThisParameterType<T> =
T extends (this: infer U, ...args: any[]) => any ? U : unknown;
使用示例:
function fn(this: { name: string }, age: number) {
console.log(this.name, age);
}
type ThisTypeOfFn = ThisParameterType<typeof fn>;
结果:
type ThisTypeOfFn = {
name: string;
};
使用场景:处理依赖 this 的老代码或库封装。
16.17 OmitThisParameter<T>
OmitThisParameter<T> 用来移除函数里的 this 参数。
function fn(this: { name: string }, age: number) {
console.log(this.name, age);
}
type FnWithoutThis = OmitThisParameter<typeof fn>;
结果类似:
type FnWithoutThis = (age: number) => void;
使用场景:把依赖 this 的函数 bind 之后再使用。
const bound = fn.bind({ name: "Tom" });
const run: OmitThisParameter<typeof fn> = bound;
16.18 工具类型组合使用
工具类型真正强大的地方在于组合。
示例 1:提取异步函数返回数据。
async function getUser() {
return {
id: "001",
name: "Tom",
};
}
type User = Awaited<ReturnType<typeof getUser>>;
示例 2:创建接口入参类型。
type User = {
id: string;
name: string;
password: string;
createdAt: string;
};
type CreateUserDto = Omit<User, "id" | "createdAt">;
结果:
type CreateUserDto = {
name: string;
password: string;
};
示例 3:更新接口入参。
type UpdateUserDto = Partial<Omit<User, "id" | "createdAt">>;
结果:
type UpdateUserDto = {
name?: string;
password?: string;
};
示例 4:状态映射。
type Status = "pending" | "success" | "error";
const statusText: Record<Status, string> = {
pending: "处理中",
success: "成功",
error: "失败",
};
示例 5:从联合类型中排除某些状态。
type Status = "pending" | "success" | "error" | "cancelled";
type ActiveStatus = Exclude<Status, "cancelled">;
16.19 小结
常用工具类型可以按用途分类记忆。
对象属性处理:
Partial<T>
Required<T>
Readonly<T>
Pick<T, K>
Omit<T, K>
Record<K, T>
联合类型处理:
Exclude<T, U>
Extract<T, U>
NonNullable<T>
函数类型处理:
ReturnType<T>
Parameters<T>
ThisParameterType<T>
OmitThisParameter<T>
构造函数处理:
ConstructorParameters<T>
InstanceType<T>
异步类型处理:
Awaited<T>
它们背后的核心能力其实就几个:
keyof
in
extends
infer
映射类型
条件类型
联合类型分发
掌握这些底层能力后,工具类型就不是黑盒了。
17. 实战建议
写 TS 时可以记住这些习惯:
- 不确定类型时,优先用
unknown,少用any。 - 对外部数据,比如接口返回值,不要盲信类型。
- 描述动态对象时,优先考虑
[key: string]: unknown。 - 对普通对象可以用
Record<string, unknown>,但要理解它不是所有对象。 - 表达多种状态时,用联合类型。
- 表达对象合并时,用交叉类型。
- 参数和返回值有类型关系时,用泛型。
- 不同参数组合对应不同返回类型时,用函数重载。
- 从复杂类型里提取子类型时,用
infer。 -
extends不只表示继承,更常用于泛型约束和条件类型判断。
18. 总结
这篇文章里,我们围绕 TS 类型系统梳理了这些重点:
[key: string]: unknown
用于描述动态属性。
"name" in obj
用于属性判断和类型收窄。
T extends SomeType
用于泛型约束。
T extends U ? X : Y
用于条件类型判断。
value is SomeType
用于自定义类型守卫。
A | B
表示联合类型,满足其中一种。
A & B
表示交叉类型,同时满足多种。
function identity<T>(value: T): T
表示泛型函数。
T extends Array<infer U> ? U : never
表示从数组中提取元素类型。
TypeScript 的难点不在于语法多,而在于要理解这些语法背后的共同目标:
用类型系统描述真实业务里的数据关系,让错误尽量在编译期暴露出来。
当你能熟练使用 extends、keyof、泛型、类型守卫、infer 这些工具时,就已经进入 TS 类型系统的核心区域了。