TS: 泛型

学 Java 的时候总会提到泛型,现在 TS 也有了,他们的用法都差不太多。泛型可以理解为广泛的类型。

为什么要用泛型

先来了解下泛型有什么用。先看下面定义的一个函数:

function itself(a: number): number {
    return a
}

itself(1) // 1

 

上面的函数就是简单的传入一个数,返回一个数,但是现在想传入字符串,返回字符串呢?或者再加传入数组,返回数组呢?这就需要用到泛型了。

function itself<T>(a: T):T {
    return a
}

console.log(itself(true)) // true
console.log(itself(1))   // 1
console.log(itself('1')) // '1'
console.log(itself([1, 2, 3])) // [1, 2, 3]

 

从上面的例子可以看到,T 有点像是一个临时变量一样,先推断出传入参数的类型,将这个类型赋值到 T T = XXX 类型,后面就直接 返回的类型 = T = XXX 类型。这么说可能不那么准确,但是可以看到 T 就是一个占位符。

如果调用函数时使用显式声明可能会更直观。

function itself<T>(a: T):T {
    return a
}

console.log(itself<boolean>(true)) // true
console.log(itself<number>(1))   // 1
console.log(itself<string>('1')) // '1'
console.log(itself<Array<number>>([1, 2, 3])) // [1, 2, 3

 

这里发现 Array<number> 里也是用了泛型的,这样可以声明数组里元素的类型。

接口与泛型

上面都是传基本类型(除了 Array),下面看看接口与泛型的配合。

interface Human {
    name: string
    age: number
}
interface Animal {
    category: string
}

function create<T>(what: T): T {
    return what
}

create<Human>({
    name: 'Jack',
    age: 18
})

create<Animal>({
    category: 'dog'
})

 

用法几乎一样,Easy~。

有了接口这个东西后,我们可以玩更高级一点的。现在我想造一个 Human,规定这个 Human 一定要有 JJ,可以这么写。

interface JJ {
    jjSize: string
    jjLength: number
}
interface Human {
    name: string
    age: number
}

function create<T extends JJ>(what: T): T {
    return what
}

create({
    name: 'Jack',
    age: 18,
    jjSize: 'large',
    jjLength: 18
})

create({
    name: 'Jack',
    age: 18
}) // 报错:没有 jjSize 和 jjLength

 

上面的这个用法叫做泛型的约束,像刚刚说的这个 Human 一定要有 JJ,这就是一个约束。

类与泛型

说完接口,肯定逃不了要说类了。我们先来看一段 JS 的代码。

function create(C) {
    return new C()
}

 

正确的理解应该是传入一个构造函数 C,然后返回 C 的实例,没了。但是这个函数啥约束都没有,如果传入一个字符串 "Hello" 呢?那不是炸了?所以我们要用 TS 里的泛型去约束它。

function create<T>(C: { new () }) {
    return new C()
}

 

两个括号里写的是 new () 说明传入的 C 是可以用 new C() 的。然后我们再加个上返回类型和传入的类型,可以写成这样。

function create<T>(c: { new (): T }): T {
    return new c()
}

 

 

posted @ 2020-02-28 09:47  simple-love  阅读(1727)  评论(0编辑  收藏  举报