03-ts-泛型

这是我参与更文挑战的第4天,活动详情查看: 更文挑战web


泛型

identity函数。 这个函数会返回任何传入它的值markdown

function identity(arg: any): any {
    return arg;
}

复制代码

使用any类型会致使这个函数能够接收任何类型的arg参数,这样就丢失了一些信息:传入的类型与返回的类型应该是相同的。若是咱们传入一个数字,咱们只知道任何类型的值都有可能被返回。ide

所以,咱们须要一种方法使返回值的类型与传入参数的类型是相同的。 这里,咱们使用了 类型变量,它是一种特殊的变量,只用于表示类型而不是值。

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

复制代码

咱们给identity添加了类型变量T。 T帮助咱们捕获用户传入的类型(好比:number),以后咱们就可使用这个类型。 以后咱们再次使用了 T当作返回值类型。如今咱们能够知道参数类型与返回值类型是相同的了。 这容许咱们跟踪函数里使用的类型的信息。函数

咱们把这个版本的identity函数叫作泛型,由于它能够适用于多个类型。 不一样于使用 any,它不会丢失信息,像第一个例子那像保持准确性,传入数值类型并返回数值类型。post

定义了泛型函数后,能够用两种方法使用。 第一种是,传入全部的参数,包含类型参数:ui

let output = identity<string>("myString"); 
复制代码

第二种方法更广泛。利用了类型推论 -- 即编译器会根据传入的参数自动地帮助咱们肯定T的类型:url

let output = identity("myString");
复制代码

注意咱们不必使用尖括号(<>)来明确地传入类型;编译器能够查看myString的值,而后把T设置为它的类型。 类型推论帮助咱们保持代码精简和高可读性。若是编译器不可以自动地推断出类型的话,只能像上面那样明确的传入T的类型,在一些复杂的状况下,这是可能出现的。spa

泛型变量

function loggingIdentity<T>(arg: T): T {
    console.log(arg.length);  // Error: T doesn't have .length
    return arg;
}


function loggingIdentity<T>(arg: T[]): T[] {
    console.log(arg.length);  // Array has a .length, so no more error
    return arg;
}

复制代码

泛型类型

泛型函数的类型与非泛型函数的类型没什么不一样,只是有一个类型参数在最前面,像函数声明同样:3d

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

let myIdentity: <T>(arg: T) => T = identity;

复制代码

咱们还可使用带有调用签名的对象字面量来定义泛型函数:code

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

let myIdentity: {<T>(arg: T): T} = identity;
复制代码

这引导咱们去写第一个泛型接口了。 咱们把上面例子里的对象字面量拿出来作为一个接口:

interface GenericIdentityFn {
    <T>(arg: T): T;
}

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

let myIdentity: GenericIdentityFn = identity;
复制代码

一个类似的例子,咱们可能想把泛型参数看成整个接口的一个参数。 这样咱们就能清楚的知道使用的具体是哪一个泛型类型(好比: GenericIdentityFn<string>而不是GenericIdentityFn)。 这样接口里的其它成员也能知道这个参数的类型了。

interface GenericIdentityFn<T> {
    (arg: T): T;
}

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

let myIdentity: GenericIdentityFn<number> = identity;
复制代码

当咱们使用 GenericIdentityFn的时候,还得传入一个类型参数来指定泛型类型(这里是:number),锁定了以后代码里使用的类型。 对于描述哪部分类型属于泛型部分来讲,理解什么时候把参数放在调用签名里和什么时候放在接口上是颇有帮助的。

除了泛型接口,咱们还能够建立泛型类。 注意,没法建立泛型枚举和泛型命名空间。

泛型约束

前面的例子,咱们想访问arg的.length属性,可是编译器并不能证实每种类型都有length属性,因此就报错了。

function loggingIdentity<T>(arg: T): T {
    console.log(arg.length);  // Error: T doesn't have .length
    return arg;
}
复制代码

如今咱们把泛型约束一下,限制函数去处理任意带有.length属性的全部类型。只要传入的类型有这个属性,咱们就容许,就是说至少包含这一属性。 为此,咱们须要列出对于T的约束要求。

改写以下:

interface Length {
    length: number
}

function loggingIdentity<T extends Length>(arg: T): T {
    console.log(arg.length);  // Now we know it has a .length property, so no more error
    return arg;
}

复制代码

如今这个泛型函数被约束了类型, 所以它再也不适用于任意类型:

例如:

loggingIdentity(123) // Error, number doesn't have a .length property

复制代码

咱们须要传入符合约束类型的值,必须包含必须的属性:

loggingIdentity({ length: 1, num: 1 })
复制代码
相关文章
相关标签/搜索