TypeScript--泛型

泛型(Generics 是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

示例

假如我们需要一个 输入数字 => 返回数字 的函数

function echoValue(args: number): number {
    return args
}

// ✅正确
echoValue(1)

// ❌错误
// Argument of type 'string' is not assignable to parameter of type 'number'.
echoValue('1')

也需要一个 输入字符串 => 返回字符串 的函数

function echoValue(args: string): string {
    return args
}

// ✅正确
echoValue('1')

// ❌错误
// Argument of type 'number' is not assignable to parameter of type 'string'.
echoValue(1)

还需要一个 输入布尔值 => 返回布尔值 的函数

function echoValue(args: boolean): boolean {
    return args
}

// ✅正确
echoValue(true)

// ❌错误
// Argument of type 'number' is not assignable to parameter of type 'boolean'.
echoValue(1)

可以发现以上三个函数,只是参数类型和返回类型不一致,看起来很冗余。

我们可以使用 泛型 解决这类问题。

function echoValue<T>(args: T): T {
    return args
}

// ✅正确
echoValue<number>(1).toFixed(2)

// ❌错误
// Property 'length' does not exist on type 'number'.
echoValue<number>(1).length

// ✅正确
echoValue<string>('1').length

// ❌错误
// Property 'toFixed' does not exist on type 'string'.
echoValue<string>('1').toFixed(2)

// ✅正确
echoValue<boolean>(true)

这里用了一个 类型 T,这个 T 是一个抽象类型,只有在调用的时候才确定它的值。

多个类型参数

定义泛型的时候,可以一次定义多个类型参数

示例

function swap<T, U>(tuple: [T, U]): [U, T] {
    return [tuple[1], tuple[0]]
}

// ✅正确
swap([3, 'three'])

// ✅正确
swap(['three', 3])

泛型类型

示例:直接定义

const echoValue: <T>(args: T) => T = function<T>(args: T): T {
    return args
}

// ✅正确
echoValue<number>(1).toFixed(2)

// ❌错误
// Property 'length' does not exist on type 'number'.
echoValue<number>(1).length

示例:使用类型别名

type EchoValue = <T>(args: T) => T

const echoValue: EchoValue = function<T>(args: T): T {
    return args
}

// ✅正确
echoValue<string>('1').length

// ❌错误
// Property 'toFixed' does not exist on type 'string'.
echoValue<string>('1').toFixed(2)

使用接口

interface EchoValue {
    <T>(args: T): T
}

const echoValue: EchoValue = function<T>(args: T): T {
    return args
}

// ✅正确
echoValue<string>('1').length

// ❌错误
// Property 'length' does not exist on type 'boolean'.
echoValue<boolean>(true).length

可以使用不同的泛型参数名,只要在数量上和使用方式上能对应上就可以

interface EchoValue {
   <T>(args: T): T
}

interface Person {
   name: string
   age: number
}

const echoValue: EchoValue = function<U>(args: U): U {
   return args
}

// ✅正确
echoValue<Person>({name: 'Tom', age: 23}).name

把泛型参数当作整个接口的一个参数,这样我们就能清楚的知道使用的具体是哪个泛型类型。

interface EchoValue<T> {
   (args: T): T
}

const echoValue: EchoValue<string> = function<T>(args: T): T {
   return args
}

// ✅正确
echoValue('abc').length

// ❌错误
// Argument of type 'number' is not assignable to parameter of type 'string'.
echoValue(1)

泛型约束

我们有时在操作某值的属性时,是事先知道它具有此属性的,但是编译器不知道

function keyToValue(obj: object, key: string): any {
    return obj[key]
}

const stu = {name: 'Tom', age: 23}

// ✅正确
// 虽然正确,但是并没有检查 'stu' 对象是否拥有 'name' 属性
keyToValue(stu, 'name')

// ⚠️不会检查'stu'对象是否拥有'gender'属性
// undefined
keyToValue(stu, 'gender')

通过泛型约束实现对这个问题的检查

function keyToValue<T, K extends keyof T>(obj: T, key: K) {
    return obj[key]
}

const stu = {name: 'Tom', age: 23}

// ✅正确
keyToValue(stu, 'name')

// ❌错误
// Argument of type '"gender"' is not assignable to parameter of type '"name" | "age"'.
keyToValue(stu, 'gender')

K 继承索引类型 keyof Tkeyof T 相当于一个由泛型变量 T 的属性名构成的联合类型。

posted @ 2021-04-16 21:59  蓦然回首!  阅读(131)  评论(0编辑  收藏  举报