TypeScript基础入门 - 泛型 - 泛型约束

{app.params.name}}{app.params.name}}{app.params.name}}

项目实践仓库

https://github.com/durban89/typescript_demo.git
tag: 1.3.4

为了保证后面的学习演示需要安装下ts-node,这样后面的每个操作都能直接运行看到输出的结果。

npm install -D ts-node

后面自己在练习的时候可以这样使用

npx ts-node 脚本路径

泛型

泛型约束

我之前分享的一个例子中,有时候想操作某类型的一组值,并且知道这组值具有什么样的属性。在loggingIdentity例子中,我们想访问arg的length属性,但是编译器并不能证明每种类型都有length属性,所以就报错了。

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


相比于操作any所有类型,我们想要限制函数去处理任意带有.length属性的所有类型。 只要传入的类型有这个属性,我们就允许,就是说至少包含这一属性。 为此,我们需要列出对于T的约束要求。为此,我们定义一个接口来描述约束条件。 创建一个包含 .length属性的接口,使用这个接口和extends关键字来实现约束:

interface LengthDefine {
    length: number;
}

function loggingIdentity<T extends LengthDefine>(arg: T): T {
    console.log(arg.length);

    return arg;
}


现在这个泛型函数被定义了约束,因此它不再是适用于任意类型:

loggingIdentity(3);


运行后会遇到如下错误提示

⨯ Unable to compile TypeScript:
src/generics_5.ts(11,17): error TS2345: Argument of type '3' is not assignable to parameter of type 'LengthDefine'.


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

loggingIdentity({length: 10, value:3});

运行后会得到如下结果

$ npx ts-node src/generics_5.ts
10

在泛型约束中使用类型参数

我们可以声明一个类型参数,且它被另一个类型参数所约束。 比如,现在我们想要用属性名从对象里获取这个属性。 并且我们想要确保这个属性存在于对象 obj上,因此我们需要在这两个类型之间使用约束。

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

let x = {a:1, b:2, c:3, d:4};
getProperty(x, "a"); // 正常
getProperty(x, "m"); // 异常

运行后得到如下错误信息

$ npx ts-node src/generics_5.ts
⨯ Unable to compile TypeScript:
src/generics_5.ts(21,16): error TS2345: Argument of type '"m"' is not assignable to parameter of type '"a" | "b" | "c" | "d"'.

在泛型里使用类类型

在TypeScript使用泛型创建工厂函数时,需要引用构造函数的类类型。比如,

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

一个更高级的例子,使用原型属性推断并约束构造函数与类实例的关系。

class Keeper1 {
    hasMask: boolean;
}

class Keeper2 {
    nameTag: string;
}

class Keeper3 {
    numLength: number;
}

class ChildrenKeeper1 extends Keeper3 {
    keeper: Keeper1;
}


class ChildrenKeeper2 extends Keeper3 {
    keeper: Keeper2;
}

function createInstance<A extends Keeper3> (c: new() => A): A {
    return new c();
}

console.log(createInstance(ChildrenKeeper1));
console.log(createInstance(ChildrenKeeper2));

运行后得到如下输出

$ npx ts-node src/generics_5.ts
ChildrenKeeper1 {}
ChildrenKeeper2 {}


感觉没在实际应用中使用,很鸡肋呀

本实例结束实践项目地址

https://github.com/durban89/typescript_demo.git
tag: 1.3.5

 

版权声明

durban创作并维护的 Gowhich博客采用创作共用保留署名-非商业-禁止演绎4.0国际许可证。

本文首发于 博客( https://www.gowhich.com ),版权所有,侵权必究。

本文永久链接: https://www.gowhich.com/blog/905


版权声明

durban创作并维护的 Gowhich博客采用创作共用保留署名-非商业-禁止演绎4.0国际许可证。

本文首发于 Gowhich博客( https://www.gowhich.com ),版权所有,侵权必究。

本文永久链接: https://www.gowhich.com/blog/905

comments powered by Disqus