应用场景
(Generics)在 TypeScript 中具有广泛的应用场景,它可以增加代码的可重用性、类型安全性和灵活性。以下是泛型常见的应用场景:
- 创造可复用的函数或类:使用泛型可以编写通用的函数或类,以适应多种类型的数据。例如,Array 类型提供了通用的泛型数组容器,可以用于存储不同类型的元素。
function printArray<T>(arr: T[]): void { for (let item of arr) { console.log(item); } } printArray([1, 2, 3]); // 打印数组元素 printArray(['a', 'b', 'c']); // 打印字符串数组元素
- 在集合类型中指定元素类型:通过使用泛型可以定义集合类型,指定其中元素的类型。例如,可以使用泛型参数
<T>
来声明一个泛型数组类型。
type MyArray<T> = T[]; const numbers: MyArray<number> = [1, 2, 3]; const strings: MyArray<string> = ['a', 'b', 'c'];
- 创建类型安全的数据结构:泛型可以用于定义类型安全的数据结构,例如栈(Stack)、队列(Queue)等。
class Stack<T> { private elements: T[] = []; push(element: T): void { this.elements.push(element); } pop(): T | undefined { return this.elements.pop(); } } const stack = new Stack<number>(); stack.push(1); stack.push(2); stack.push(3); console.log(stack.pop()); // 输出 3 console.log(stack.pop()); // 输出 2
- 在 React 中编写可复用组件:泛型可以用于编写通用的高阶组件(Higher-order Components,HOC)。HOC 是一个接受一个组件作为参数并返回一个新组件的函数。通过使用泛型,可以使 HOC 更加灵活,适用于不同类型的组件。
function withLoading<T>(Component: React.ComponentType<T>) { return function WithLoading(props: T) { // 添加 loading 逻辑 return <Component {...props} />; }; } const MyComponent = withLoading(SomeComponent); // 使用泛型为 SomeComponent 添加 Loading 功能
- 创建可扩展的抽象函数或类:通过泛型可以创建可扩展的抽象函数或类,以便其他开发人员可以通过扩展泛型类型参数来添加额外的功能。
泛型约束(Generic Constraints)
用于限制泛型类型参数的类型范围。通过使用泛型约束,可以指定传递给泛型的类型参数必须满足特定的条件,从而增加代码的类型安全性。
以下是几种常见的情况,可以考虑使用泛型约束:
- 限制类型参数必须具有某种属性或方法:有时候我们需要确保泛型类型参数包含特定的属性或方法。例如,我们想写一个函数来获取数组的第一个元素,但不确定数组中元素的类型。这时可以使用泛型约束来约束类型参数必须具有
length
属性和索引访问方法,以确保参数是一个数组类型:
function getFirstElement<T extends ArrayLike<any>>(arr: T): T[number] | undefined { return arr.length > 0 ? arr[0] : undefined; } const result = getFirstElement([1, 2, 3]); // result 的类型为 number
- 将泛型类型参数约束为特定的类型或类型范围:有时候我们需要限制泛型类型参数必须是某个特定类型或满足某个类型范围。例如,我们想写一个函数,接受两个参数并返回它们的和,但限制参数必须是数字类型:
function add<T extends number>(a: T, b: T): T { return a + b; } const result = add(2, 3); // result 的类型为 number
在上述例子中,使用 extends number
泛型约束,确保 a
和 b
的类型参数必须是数字类型。
- 使用多个泛型类型参数之间的关系:有时候我们需要在泛型类型参数之间建立关系,例如一个类型参数必须是另一个类型参数的子类型。这种情况下,我们可以使用泛型约束来描述关系。
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const person = { name: 'John', age: 30, }; const name = getProperty(person, 'name'); // name 的类型为 string const age = getProperty(person, 'age'); // age 的类型为 number
在上述例子中,使用 K extends keyof T
泛型约束,确保 key
的类型参数是对象 T
的键名之一,从而确保获取属性值的键名在对象上有效。
总而言之,使用泛型约束可以在泛型代码中增加类型的限制和约束,提高代码的类型安全性和可读性。泛型约束使得泛型类型参数更加灵活和有用。