Appearance
TypeScript 泛型
泛型是 TypeScript 中一个强大的特性,它允许我们创建可重用的组件,这些组件可以处理多种类型的数据,同时保持类型安全。
泛型基础
泛型函数
typescript
// 不使用泛型的函数
function identity1(arg: number): number {
return arg;
}
// 使用 any 类型的函数(丢失了类型信息)
function identity2(arg: any): any {
return arg;
}
// 使用泛型的函数
function identity<T>(arg: T): T {
return arg;
}
// 调用泛型函数 - 明确指定类型
let output1 = identity<string>("myString");
// 调用泛型函数 - 类型推断
let output2 = identity("myString"); // TypeScript 自动推断 T 为 string
泛型类型变量
typescript
function loggingIdentity<T>(arg: T): T {
// console.log(arg.length); // 错误:T 不一定有 .length 属性
return arg;
}
// 使用数组类型的泛型函数
function loggingIdentity2<T>(arg: T[]): T[] {
console.log(arg.length); // 正确:T[] 有 .length 属性
return arg;
}
// 等价于上面的写法
function loggingIdentity3<T>(arg: Array<T>): Array<T> {
console.log(arg.length);
return arg;
}
泛型类型
泛型接口
typescript
interface GenericIdentityFn<T> {
(arg: T): T;
}
function identity<T>(arg: T): T {
return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;
泛型类
typescript
class GenericNumber<T> {
zeroValue: T;
add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
// 泛型类的字符串版本
let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "";
stringNumeric.add = function(x, y) { return x + y; };
泛型约束
基本约束
typescript
interface Lengthwise {
length: number;
}
function loggingIdentity4<T extends Lengthwise>(arg: T): T {
console.log(arg.length); // 现在可以安全地访问 .length 属性
return arg;
}
loggingIdentity4({ length: 10, value: 3 }); // 正确
// loggingIdentity4(3); // 错误:number 类型没有 .length 属性
在泛型约束中使用类型参数
typescript
function getProperty<T, K extends keyof T>(obj: T, key: K) {
return obj[key];
}
let x = { a: 1, b: 2, c: 3, d: 4 };
getProperty(x, "a"); // 正确
// getProperty(x, "m"); // 错误:'m' 不是 'a' | 'b' | 'c' | 'd' 中的任何一种
在泛型里使用类类型
typescript
function create<T>(c: { new(): T }): T {
return new c();
}
// 使用更复杂的工厂函数
class BeeKeeper {
hasMask: boolean = true;
}
class ZooKeeper {
nametag: string = "Mikle";
}
class Animal {
numLegs: number = 4;
}
class Bee extends Animal {
keeper: BeeKeeper = new BeeKeeper();
}
class Lion extends Animal {
keeper: ZooKeeper = new ZooKeeper();
}
function createInstance<A extends Animal>(c: new () => A): A {
return new c();
}
createInstance(Lion).keeper.nametag; // 类型为 string
createInstance(Bee).keeper.hasMask; // 类型为 boolean
高级泛型模式
泛型工具类型
TypeScript 提供了一些内置的泛型工具类型:
typescript
// Partial<T> - 将所有属性变为可选
interface Todo {
title: string;
description: string;
}
function updateTodo(todo: Todo, fieldsToUpdate: Partial<Todo>) {
return { ...todo, ...fieldsToUpdate };
}
// Pick<T, K> - 从 T 中选择部分属性
type TodoPreview = Pick<Todo, "title">;
const todo3: TodoPreview = {
title: "Clean room"
};
// Record<K, T> - 创建一个对象类型,键为 K,值为 T
interface PageInfo {
title: string;
}
type Page = "home" | "about" | "contact";
const x: Record<Page, PageInfo> = {
about: { title: "about" },
contact: { title: "contact" },
home: { title: "home" }
};
// Omit<T, K> - 从 T 中省略部分属性
interface Todo2 {
title: string;
description: string;
completed: boolean;
}
type TodoPreview2 = Omit<Todo2, "description">;
const todo4: TodoPreview2 = {
title: "Clean room",
completed: false,
};
// Exclude<T, U> - 从 T 中排除 U
type T0 = Exclude<"a" | "b" | "c", "a">; // "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">; // "c"
type T2 = Exclude<string | number | (() => void), Function>; // string | number
// Extract<T, U> - 从 T 中提取 U
type T3 = Extract<"a" | "b" | "c", "a" | "f">; // "a"
type T4 = Extract<string | number | (() => void), Function>; // () => void
// NonNullable<T> - 从 T 中排除 null 和 undefined
type T5 = NonNullable<string | number | undefined>; // string | number
type T6 = NonNullable<string[] | null | undefined>; // string[]
// ReturnType<T> - 获取函数返回值类型
type T7 = ReturnType<() => string>; // string
type T8 = ReturnType<(s: string) => void>; // void
type T9 = ReturnType<<T>() => T>; // {}
type T10 = ReturnType<<T extends U, U extends number[]>() => T>; // number[]
// InstanceType<T> - 获取构造函数实例类型
class C {
x = 0;
y = 0;
}
type T11 = InstanceType<typeof C>; // C
type T12 = InstanceType<any>; // any
type T13 = InstanceType<never>; // never
// type T14 = InstanceType<string>; // 错误
条件类型
typescript
// 条件类型的基本形式:T extends U ? X : Y
type TypeName<T> = T extends string ? "string" :
T extends number ? "number" :
T extends boolean ? "boolean" :
"other";
type T0 = TypeName<string>; // "string"
type T1 = TypeName<"a">; // "string"
type T2 = TypeName<true>; // "boolean"
// 分配条件类型
type BoxedValue<T> = { value: T };
type BoxedArray<T> = { array: T[] };
type Boxed<T> = T extends any[] ? BoxedArray<T[number]> : BoxedValue<T>;
type T20 = Boxed<string>; // BoxedValue<string>
type T21 = Boxed<number[]>; // BoxedArray<number>
type T22 = Boxed<string | number[]>; // BoxedValue<string> | BoxedArray<number>
泛型条件类型
typescript
// infer 关键字用于推断类型
type Flatten<T> = T extends Array<infer Item> ? Item : T;
type T1 = Flatten<number[]>; // number
type T2 = Flatten<string>; // string
type T3 = Flatten<readonly string[]>; // string
// 更复杂的 infer 使用
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : any;
type T4 = GetReturnType<() => number>; // number
type T5 = GetReturnType<(x: string) => boolean>; // boolean
泛型与类、接口的高级应用
泛型约束与默认类型
typescript
// 泛型参数的默认类型
interface A<T = string> {
name: T;
}
const a1: A<number> = { name: 123 }; // T 是 number
const a2: A = { name: "hello" }; // T 是默认的 string
多泛型参数
typescript
// 使用多个泛型参数
function processTuple<T, U>(tuple: [T, U]): [U, T] {
return [tuple[1], tuple[0]];
}
const result = processTuple(["hello", 42]); // [number, string]
泛型工厂模式
typescript
interface IFactory<T> {
create(): T;
}
class Foo {
constructor(public name: string) {}
}
class Bar {
constructor(public age: number) {}
}
const fooFactory: IFactory<Foo> = {
create: () => new Foo("default")
};
const barFactory: IFactory<Bar> = {
create: () => new Bar(25)
};
泛型最佳实践
类型安全的 CRUD 操作
typescript
interface Entity {
id: number;
}
interface User extends Entity {
name: string;
email: string;
}
interface Product extends Entity {
title: string;
price: number;
}
class Repository<T extends Entity> {
private entities: T[] = [];
add(entity: T): T {
this.entities.push(entity);
return entity;
}
findById(id: number): T | undefined {
return this.entities.find(e => e.id === id);
}
getAll(): T[] {
return [...this.entities];
}
update(id: number, update: Partial<T>): T | undefined {
const index = this.entities.findIndex(e => e.id === id);
if (index !== -1) {
this.entities[index] = { ...this.entities[index], ...update };
return this.entities[index];
}
return undefined;
}
delete(id: number): boolean {
const initialLength = this.entities.length;
this.entities = this.entities.filter(e => e.id !== id);
return initialLength > this.entities.length;
}
}
// 使用示例
const userRepository = new Repository<User>();
const user = userRepository.add({ id: 1, name: "John", email: "john@example.com" });
const productRepository = new Repository<Product>();
const product = productRepository.add({ id: 1, title: "Laptop", price: 999 });
小结
泛型是 TypeScript 中非常强大的特性,它允许我们编写可重用且类型安全的代码。通过泛型,我们可以创建适用于多种类型的组件,同时保留类型信息,提高代码的灵活性和可维护性。掌握泛型的概念和用法对于编写高质量的 TypeScript 代码至关重要。