高级类型
TypeScript 的高级类型系统提供了强大的类型操作能力,包括联合类型、交叉类型、条件类型、映射类型等。
联合类型(Union Types)
联合类型表示值可以是多种类型之一。
基本语法
type ID = string | number;
function printId(id: ID): void {
console.log(`ID: ${id}`);
}
printId(101); // ✅
printId("abc"); // ✅类型收窄
function process(value: string | number): string {
// 类型收窄
if (typeof value === "string") {
return value.toUpperCase(); // value 是 string
}
return value.toFixed(2); // value 是 number
}可辨识联合(Discriminated Unions)
interface Circle {
kind: "circle";
radius: number;
}
interface Rectangle {
kind: "rectangle";
width: number;
height: number;
}
interface Triangle {
kind: "triangle";
base: number;
height: number;
}
type Shape = Circle | Rectangle | Triangle;
function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "rectangle":
return shape.width * shape.height;
case "triangle":
return (shape.base * shape.height) / 2;
}
}交叉类型(Intersection Types)
交叉类型将多个类型合并为一个。
interface Name {
firstName: string;
lastName: string;
}
interface Age {
age: number;
}
type Person = Name & Age;
const person: Person = {
firstName: "John",
lastName: "Doe",
age: 30
};合并接口
interface Printable {
print(): void;
}
interface Loggable {
log(): void;
}
type PrintableLoggable = Printable & Loggable;
const obj: PrintableLoggable = {
print() { console.log("printing"); },
log() { console.log("logging"); }
};与联合类型结合
type SuccessResponse = {
status: "success";
data: unknown;
};
type ErrorResponse = {
status: "error";
message: string;
};
type Response = SuccessResponse | ErrorResponse;
// 带有通用属性的响应
type TimestampedResponse = Response & { timestamp: Date };条件类型(Conditional Types)
条件类型根据条件选择类型。
基本语法
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false实用示例
// 提取函数返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
function greet(): string {
return "hello";
}
type GreetReturn = ReturnType<typeof greet>; // string
// 提取 Promise 解析类型
type Awaited<T> = T extends Promise<infer U> ? U : T;
type A = Awaited<Promise<string>>; // string
type B = Awaited<Promise<number>>; // number
type C = Awaited<string>; // stringinfer 关键字
infer 用于在条件类型中推断类型:
// 提取数组元素类型
type ElementType<T> = T extends (infer E)[] ? E : never;
type A = ElementType<string[]>; // string
type B = ElementType<number[]>; // number
// 提取函数第一个参数类型
type FirstArg<T> = T extends (first: infer F, ...args: any[]) => any ? F : never;
type C = FirstArg<(name: string, age: number) => void>; // string
// 提取 Promise 类型
type UnwrapPromise<T> = T extends Promise<infer U> ? UnwrapPromise<U> : T;
type D = UnwrapPromise<Promise<Promise<string>>>; // string分布式条件类型
当条件类型作用于联合类型时,会自动分布:
type ToArray<T> = T extends any ? T[] : never;
type Result = ToArray<string | number>;
// string[] | number[](而不是 (string | number)[])
// 阻止分布
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
type Result2 = ToArrayNonDist<string | number>;
// (string | number)[]映射类型(Mapped Types)
映射类型基于现有类型创建新类型。
基本语法
type Readonly<T> = {
readonly [K in keyof T]: T[K];
};
type Partial<T> = {
[K in keyof T]?: T[K];
};
interface User {
name: string;
age: number;
}
type ReadonlyUser = Readonly<User>;
// { readonly name: string; readonly age: number }
type PartialUser = Partial<User>;
// { name?: string; age?: number }键名重映射(Key Remapping)
TypeScript 4.1+ 支持使用 as 重映射键名:
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
interface Person {
name: string;
age: number;
}
type PersonGetters = Getters<Person>;
// { getName: () => string; getAge: () => number }过滤属性
// 移除指定属性
type RemoveKind<T> = {
[K in keyof T as Exclude<K, "kind">]: T[K];
};
// 只保留函数属性
type FunctionsOnly<T> = {
[K in keyof T as T[K] extends Function ? K : never]: T[K];
};
interface Example {
name: string;
greet(): void;
calculate(x: number): number;
}
type ExampleFunctions = FunctionsOnly<Example>;
// { greet: () => void; calculate: (x: number) => number }修饰符操作
// 移除 readonly
type Mutable<T> = {
-readonly [K in keyof T]: T[K];
};
// 移除可选
type Required<T> = {
[K in keyof T]-?: T[K];
};
// 添加可选
type Optional<T> = {
[K in keyof T]+?: T[K];
};模板字面量类型(Template Literal Types)
TypeScript 4.1+ 支持模板字面量类型。
基本语法
type World = "world";
type Greeting = `hello ${World}`; // "hello world"
type EmailLocale = "en" | "zh" | "ja";
type EmailEvent = "welcome" | "goodbye";
type EmailId = `${EmailLocale}_${EmailEvent}`;
// "en_welcome" | "en_goodbye" | "zh_welcome" | "zh_goodbye" | "ja_welcome" | "ja_goodbye"内置字符串操作类型
type Upper = Uppercase<"hello">; // "HELLO"
type Lower = Lowercase<"HELLO">; // "hello"
type Cap = Capitalize<"hello">; // "Hello"
type Uncap = Uncapitalize<"Hello">; // "hello"实用模式
// CSS 属性名
type CSSProperty = `${string}-${string}`;
const prop: CSSProperty = "font-size"; // ✅
// 事件处理器
type EventHandler<T extends string> = `on${Capitalize<T>}`;
type ClickHandler = EventHandler<"click">; // "onClick"
// API 路由
type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE";
type Route = "/users" | "/posts";
type Endpoint = `${HTTPMethod} ${Route}`;索引访问类型(Indexed Access Types)
interface User {
name: string;
age: number;
address: {
city: string;
country: string;
};
}
// 访问单个属性类型
type UserName = User["name"]; // string
// 访问嵌套属性类型
type City = User["address"]["city"]; // string
// 联合索引
type NameOrAge = User["name" | "age"]; // string | number
// 使用 keyof
type UserValues = User[keyof User]; // string | number | { city: string; country: string }数组索引类型
const colors = ["red", "green", "blue"] as const;
type Color = typeof colors[number]; // "red" | "green" | "blue"
// 元组索引
type Tuple = [string, number, boolean];
type First = Tuple[0]; // string
type Second = Tuple[1]; // number类型操作工具
keyof 操作符
interface User {
name: string;
age: number;
email: string;
}
type UserKeys = keyof User; // "name" | "age" | "email"
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}typeof 操作符
const config = {
url: "https://api.example.com",
timeout: 5000
};
type Config = typeof config;
// { url: string; timeout: number }
// 函数类型
function greet(name: string): string {
return `Hello, ${name}`;
}
type GreetFn = typeof greet;
// (name: string) => string组合使用
const StatusCode = {
OK: 200,
NotFound: 404,
ServerError: 500
} as const;
type StatusCodeKey = keyof typeof StatusCode;
// "OK" | "NotFound" | "ServerError"
type StatusCodeValue = typeof StatusCode[StatusCodeKey];
// 200 | 404 | 500实用类型模式
深度 Partial
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};
interface Config {
server: {
host: string;
port: number;
};
database: {
url: string;
};
}
type PartialConfig = DeepPartial<Config>;
// 所有嵌套属性都变为可选深度 Readonly
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};路径类型
type PathKeys<T, Prefix extends string = ""> = T extends object
? {
[K in keyof T]: K extends string
? Prefix extends ""
? K | `${K}.${PathKeys<T[K], "">}`
: `${Prefix}.${K}` | `${Prefix}.${K}.${PathKeys<T[K], "">}`
: never;
}[keyof T]
: never;最佳实践
- 使用可辨识联合:处理复杂状态时优先使用
- 避免过度嵌套:条件类型不要嵌套太深
- 利用内置工具类型:不要重复造轮子
- 类型测试:使用
type-fest等库验证类型 - 保持可读性:复杂类型使用类型别名分解
// 推荐:分解复杂类型
type ExtractString<T> = T extends string ? T : never;
type ExtractFunction<T> = T extends Function ? T : never;
type StringsAndFunctions<T> = ExtractString<T> | ExtractFunction<T>;
// 不推荐:一行写完所有逻辑
type Complex<T> = T extends string ? T : T extends Function ? T : never;