typescript的泛型看这篇就能入门了

26 阅读2分钟

image.png

在查阅一些使用typescript编写的优秀库源码时,我们会发现泛型被广泛运用,本文浅浅地学习下它是如何提高代码的复用性、类型安全性,并且使得函数和组件更加灵活。

泛型的目的

  • 提高代码复用性: 不必为每种类型编写单独的函数或类,你可以写一次泛型代码,然后用不同的类型来实例化。
  • 提供类型安全: 通过在编译时进行类型检查,减少因类型错误引起的运行时错误。
  • 增加代码清晰度: 泛型可以帮助你创建更清晰和自描述的代码。类型参数作为一种文档形式,指示了函数、类或接口的使用方式。
  • 支持灵活性和可扩展性: 泛型使得代码更加灵活、易于扩展,因为新的类型可以很容易地与现有的泛型结构一起使用,而无需修改原始代码。

泛型的应用

1. 函数和方法

使用泛型函数,例如,一个可以返回任何类型参数的同类型值的 identity 函数。

function identity<T>(arg: T): T {
  return arg;
}

let output1 = identity<string>("myString");  // 类型为 'string'
let output2 = identity<number>(100);         // 类型为 'number'

在这里,<T> 允许你捕获调用函数时提供的类型(例如,stringnumber)。

2. 接口

定义一个泛型接口,使得这个接口可以与任何数据类型一起使用。

interface GenericInterface<T> {
  value: T;
  doSomething: (arg: T) => void;
}

// 实现接口的类
class ExampleClass implements GenericInterface<number> {
  value = 10;
  doSomething(arg: number): void {
    console.log(`Doing something with ${arg}`);
  }
}

let myExample = new ExampleClass();
myExample.doSomething(5);  // 输出: Doing something with 5

3.

创建一个泛型类,例如,一个栈数据结构:

class GenericStack<T> {
  private stack: T[] = [];
  
  push(item: T) {
    this.stack.push(item);
  }
  
  pop(): T | undefined {
    return this.stack.pop();
  }
}

let numberStack = new GenericStack<number>();
numberStack.push(1);
console.log(numberStack.pop());  // 输出: 1

let stringStack = new GenericStack<string>();
stringStack.push("hello");
console.log(stringStack.pop());  // 输出: hello

在这个例子中,GenericStack 类可以处理任何类型的元素。

4. 类型别名

泛型类型别名可以定义一个结构,这个结构可以适用于多种类型。

type Response<T> = {
  status: number;
  data: T;
};

let response1: Response<string> = {
  status: 200,
  data: "Success"
};

let response2: Response<number[]> = {
  status: 200,
  data: [1, 2, 3]
};

在这里,Response<T> 类型别名用于表示包含任意类型数据的响应对象。

5. 高阶组件/函数

在 React 中,你可能会遇到需要定义高阶组件(HOC)的情况,这些组件可以接收任意类型的 props

import React from 'react';

// 这是一个简单的泛型高阶组件
function withLogging<T>(WrappedComponent: React.ComponentType<T>) {
  return (props: T) => {
    console.log('Current props:', props);
    return <WrappedComponent {...props} />;
  };
}

// 假设你有一个组件
const MyComponent = (props: { message: string }) => <div>{props.message}</div>;

// 使用泛型高阶组件包装 MyComponent
const MyComponentWithLogging = withLogging(MyComponent);

// 渲染组件,并自动打印 props
<MyComponentWithLogging message="Hello, world!" />;

在这个例子中,withLogging 是一个高阶组件,它接受任何类型的 props 并打印它们,然后返回一个新的组件。