TypeScript学习笔记(五) | 泛型genericity

180 阅读3分钟

| 什么是泛型

泛型是可以在保证类型安全的前提下,让函数可以多种类型一起工作实现复用

泛型常用于函数、接口、类中

为了让函数可以接受任意类型,为什么不将参数改为any呢?

使用any会失去TS的类型保护,类型不安全,这样就本末倒置了

在其他编程语言中,泛型都是用来实现可复用组件的主要工具之一

// 使用泛型来创建一个函数
function id<Type>(value: Type): Type {
    return value;
}
// 调用泛型函数
const test1 = id<number>(1);
const test2 = id<string>('1');

console.log(test1); // 1
console.log(test2); // '1'

// 简写
const test1 = id(1); // 1
const test2 = id('1'); // '1'

| 泛型约束

默认情况下,因为泛型函数的变量Type可以代表多个类型,这就导致无法访问任何属性

如无法保证当前值一定存在length属性

image.png
此时需要使用泛型约束来指定类型取值范围

替换方式

  1. 指定更加具体的类型

比如将类型改为Type[],即Type类型的数组,因为数组必然有length属性

function id<Type>(value: Type[]): Type[] {
    console.log(value.length);
    return value;
}
  1. 添加约束

①创建一个接口用来提供length属性

②通过extends来使用该接口,表面传入的参数必须要有length属性

interface ILength {
    length: number;
}
function id<Type extends ILength>(value: Type): Type {
    console.log(value.length);
    return value;
}
id([1]); // 必须要有length属性,比如数组

| 多个泛型变量

泛型的类型变量可以有多个,并且变量之间可以约束

示例

function getProp<Type, Key extends keyof Type>(obj: Type, key: Key) {
    return obj[key];
}
let person = {
    name: 'lzz',
    age: 18,
};
console.log(getProp(person, 'age'));

示例中使用两个变量类型,Type和Key

keyof关键字用于两个变量之间的约束,Key只能是Type中的属性

| 泛型之于接口

接口也可以配合泛型使用,增加灵活性、复用性

在接口后面添加<类型变量>即可

interface idFunc<Type> {
    id: (value: Type) => Type;
    ids: () => Type[];
}
// 指定类型为number
let obj: idFunc<number> = {
    id(value) {
        return value;
    },
    ids() {
        return [1, 2, 3]; // number数组
    },
};

事实上,ts中的数组也是一个泛型接口

当我们使用数组时,TS会根据数组的不同类型来自动将类型变量设置为相应类型

| 泛型之于类

class也可以配合泛型来使用

比如React的class组件的基类Component就是泛型类,不同组件有不同的props和state

image.png

创建泛型类

class GenericNumber<Type> {
    defaultValue: Type;
    add: (a: Type, b: Type) => Type;
}
let myNumber = new GenericNumber<string>(); // 可传类型,也可省略
myNumber.defaultValue = '10'
myNumber.add = (a, b) => a + b;

| 泛型工具

TS内置了一些常用工具类型,来简化TS中常见操作

常见工具类型有

  1. Partial
  2. Readonly
  3. Pick
  4. Record

Partial 创建可选

用来构建一个类型,将Type的所有属性设置为可选

比如有一个接口A,你要创建一个结构与接口A一样类型B,但属性是可选的,就用Partial

interface A {
    id: number;
    name: string;
}
type B = Partial<A>;
let test: B = {
    id: 1 // id或name属性可选
};

Readonly 只读

顾名思义,就是将Type所有属性设置为只读

interface A {
    id: number;
    name: string;
}
type B = Readonly<A>;
let test: B = {
    id: 1,
    name: 'zz',
};
test.id = 2 // 报错,Cannot assign to 'id' because it is a read-only property.

Pick 挑选属性

从Type中选择一组属性来构造新的Type

Pick<选择谁的属性, '属性1' | '属性2' >

interface A {
    id: number;
    name: string;
    age: number;
}

type B = Pick<A, 'id' | 'age'>;
let test: B = {
    id: 1,
    age: 18,
};

Record 构造对象类型

构造一个对象类型,属性键为Keys, 属性类型为Type

示例,构建一个含有age,name,id,类型全为string的对象

type Obj = Record<'age' | 'name' | 'id', string>;
let person: Obj = {
    id: '233',
    age: '1',
    name: 'zz',
};