TypeScript 高级类型详解
TypeScript 的类型系统是其最强大的特性之一。本文将深入探讨 TypeScript 的高级类型,帮助你写出更类型安全、更易维护的代码。
1. 泛型(Generics)
泛型是 TypeScript 中最强大的特性之一,它允许我们编写可重用的代码。
基础泛型
// 泛型函数
function identity<T>(arg: T): T {
return arg;
}
// 使用
const num = identity<number>(42);
const str = identity<string>("hello");
// 类型推断
const inferred = identity(42); // T 被推断为 number
泛型约束
interface HasLength {
length: number;
}
// 约束 T 必须具有 length 属性
function logLength<T extends HasLength>(arg: T): T {
console.log(arg.length);
return arg;
}
logLength("hello"); // ✅ 字符串有 length
logLength([1, 2, 3]); // ✅ 数组有 length
logLength({ length: 10 }); // ✅ 对象有 length
// logLength(42); // ❌ number 没有 length
泛型接口
interface GenericResponse<T> {
data: T;
status: number;
message: string;
}
// 使用
interface User {
id: number;
name: string;
}
const userResponse: GenericResponse<User> = {
data: { id: 1, name: "John" },
status: 200,
message: "Success"
};
泛型类
class GenericStack<T> {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
peek(): T | undefined {
return this.items[this.items.length - 1];
}
}
// 使用
const numberStack = new GenericStack<number>();
numberStack.push(1);
numberStack.push(2);
const top = numberStack.pop(); // number | undefined
2. 条件类型(Conditional Types)
条件类型允许我们根据类型关系选择类型。
// 基础条件类型
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// 实际应用:根据类型返回不同结果
type MessageOf<T> = T extends { message: unknown } ? T["message"] : never;
interface Email {
message: string;
}
interface Dog {
bark(): void;
}
type EmailMessage = MessageOf<Email>; // string
type DogMessage = MessageOf<Dog>; // never
infer 关键字
// 提取函数返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
function greet() {
return "hello";
}
type GreetReturn = ReturnType<typeof greet>; // string
// 提取 Promise 解析类型
type Awaited<T> = T extends Promise<infer U> ? U : T;
type PromiseValue = Awaited<Promise<string>>; // string
3. 映射类型(Mapped Types)
映射类型允许我们基于旧类型创建新类型。
// 将所有属性变为可选
type Partial<T> = {
[P in keyof T]?: T[P];
};
// 将所有属性变为必需
type Required<T> = {
[P in keyof T]-?: T[P];
};
// 将所有属性变为只读
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
// 使用
interface User {
name: string;
age: number;
email?: string;
}
type PartialUser = Partial<User>;
// { name?: string; age?: number; email?: string; }
type RequiredUser = Required<User>;
// { name: string; age: number; email: string; }
type ReadonlyUser = Readonly<User>;
// { readonly name: string; readonly age: number; readonly email?: string; }
键重映射
// 添加前缀
type AddPrefix<T, P extends string> = {
[K in keyof T as `${P}${string & K}`]: T[K];
};
interface Config {
host: string;
port: number;
}
type EnvConfig = AddPrefix<Config, "APP_">;
// { APP_host: string; APP_port: number; }
// 过滤特定类型
type StringProperties<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K];
};
interface Person {
name: string;
age: number;
email: string;
}
type StringProps = StringProperties<Person>;
// { name: string; email: string; }
4. 模板字面量类型
type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<"click">; // "onClick"
type HoverEvent = EventName<"hover">; // "onHover"
// 组合多个字面量
type Alignment = "top" | "bottom" | "left" | "right";
type MarginProperty = `margin${Capitalize<Alignment>}`;
// "marginTop" | "marginBottom" | "marginLeft" | "marginRight"
5. 实用工具类型
Pick 和 Omit
interface User {
id: number;
name: string;
email: string;
password: string;
createdAt: Date;
}
// 选取特定属性
type PublicUser = Pick<User, "id" | "name" | "email">;
// { id: number; name: string; email: string; }
// 排除特定属性
type SafeUser = Omit<User, "password">;
// { id: number; name: string; email: string; createdAt: Date; }
Record
// 创建具有特定键和值类型的对象
type PageInfo = {
title: string;
path: string;
};
type PageMap = Record<string, PageInfo>;
const pages: PageMap = {
home: { title: "Home", path: "/" },
about: { title: "About", path: "/about" },
};
Exclude 和 Extract
type T = "a" | "b" | "c" | "d";
// 排除特定类型
type ExcludeCD = Exclude<T, "c" | "d">; // "a" | "b"
// 提取特定类型
type ExtractAB = Extract<T, "a" | "b">; // "a" | "b"
Parameters 和 ReturnType
function greet(name: string, age: number): string {
return `Hello ${name}, you are ${age}`;
}
// 获取参数类型
type GreetParams = Parameters<typeof greet>;
// [name: string, age: number]
// 获取返回类型
type GreetReturn = ReturnType<typeof greet>;
// string
6. 类型守卫(Type Guards)
// typeof 类型守卫
function processValue(value: string | number) {
if (typeof value === "string") {
return value.toUpperCase(); // TypeScript 知道这里是 string
}
return value.toFixed(2); // TypeScript 知道这里是 number
}
// instanceof 类型守卫
class Dog {
bark() {
console.log("Woof!");
}
}
class Cat {
meow() {
console.log("Meow!");
}
}
function makeSound(animal: Dog | Cat) {
if (animal instanceof Dog) {
animal.bark();
} else {
animal.meow();
}
}
// 自定义类型守卫
interface Fish {
swim(): void;
}
interface Bird {
fly(): void;
}
function isFish(animal: Fish | Bird): animal is Fish {
return (animal as Fish).swim !== undefined;
}
function move(animal: Fish | Bird) {
if (isFish(animal)) {
animal.swim();
} else {
animal.fly();
}
}
7. 联合类型和交叉类型
联合类型
type Status = "pending" | "success" | "error";
type ID = string | number;
interface SuccessResult {
status: "success";
data: unknown;
}
interface ErrorResult {
status: "error";
error: string;
}
type Result = SuccessResult | ErrorResult;
function handleResult(result: Result) {
if (result.status === "success") {
console.log(result.data);
} else {
console.error(result.error);
}
}
交叉类型
interface HasName {
name: string;
}
interface HasAge {
age: number;
}
type Person = HasName & HasAge;
// { name: string; age: number; }
const person: Person = {
name: "John",
age: 30,
};
8. 递归类型
// 树形结构
type TreeNode<T> = {
value: T;
children?: TreeNode<T>[];
};
const tree: TreeNode<string> = {
value: "root",
children: [
{
value: "child1",
children: [
{ value: "grandchild1" },
{ value: "grandchild2" },
],
},
{ value: "child2" },
],
};
// JSON 类型
type JSONValue =
| string
| number
| boolean
| null
| JSONValue[]
| { [key: string]: JSONValue };
const json: JSONValue = {
name: "John",
age: 30,
hobbies: ["reading", "gaming"],
address: {
city: "New York",
zip: "10001",
},
};
9. 类型挑战示例
深度 Readonly
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object
? DeepReadonly<T[P]>
: T[P];
};
interface NestedObject {
a: {
b: {
c: string;
};
};
}
type ReadonlyNested = DeepReadonly<NestedObject>;
// 所有嵌套属性都变为 readonly
元组转联合类型
type TupleToUnion<T extends readonly unknown[]> = T[number];
type Result = TupleToUnion<["a", "b", "c"]>;
// "a" | "b" | "c"
可选链式调用类型
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
// 使用
interface AppConfig {
server: {
host: string;
port: number;
ssl: {
cert: string;
key: string;
};
};
}
type PartialConfig = DeepPartial<AppConfig>;
// 所有属性都变为可选,包括嵌套的
总结
TypeScript 高级类型的核心概念:
- 泛型 - 编写可重用的类型安全代码
- 条件类型 - 基于类型关系选择类型
- 映射类型 - 基于旧类型创建新类型
- 模板字面量类型 - 操作字符串类型
- 类型守卫 - 运行时类型检查
- 联合/交叉类型 - 组合类型
掌握这些高级类型,你将能够:
- 构建更类型安全的应用
- 减少运行时错误
- 提高代码可维护性
- 编写更好的库类型定义
本文首发于技术博客,转载请注明出处。