经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » TypeScript » 查看文章
TypeScript泛型的使用详细介绍
来源:jb51  时间:2022/9/20 9:28:54  对本文有异议

情景再现

这里针对一种情况,也是非常常见的一种情况:那就是

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

就是我接收一个number类型的参数,同时也返回一个number,那如果现在我想要接收一个string类型,同时也返回一个string,那么我就要再写一个函数像这样:

function identity2(arg: string): string{
    return arg;
}

那如果我现在想要void类型…??????

可能大家会想,那全部都变成any不就行了?像下面这样

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

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

那这样不就与我们一开始的设想不一致了吗?我传入Number,返回string,也不会报错呀!??

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

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

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

??‍??我们把这个版本的identity函数叫做泛型,因为它可以适用于多个类型。 不同于使用any,它不会丢失信息,像第一个例子那像保持准确性,传入数值类型并返回数值类型。

使用泛型

第一种是,传入所有的参数,包含类型参数

第二种方法更普遍。利用了类型推论 – 即编译器会根据传入的参数自动地帮助我们确定T的类型

function identity<T>(arg: T): T {
    return arg;
}
let output = identity<string>("myString");
let output2 = identity("myString2"); 
console.log(output);
console.log(output2);

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

泛型类型

我们研究一下函数本身的类型,以及如何创建泛型接口。

来看看泛型类型不同的展现方式:

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

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

function identity<T>(arg: T): T {
    return arg;
}
let myFunction: <T>(arg:T) => T = identity;
let myIdentity: <U>(arg: U) => U = identity;

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

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;

可不可以详细一点

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

interface GenericIdentityFn<T> {
    (arg: T): T;
}
function identity<T>(arg: T): T {
    return arg;
}
let myIdentity: GenericIdentityFn<number> = identity;

泛型类

我等这个泛型类等了好久好久??????

泛型类看上去与泛型接口差不多。 泛型类使用(<>)括起泛型类型,跟在类名后面。

// 泛型类
class GenericNumber<T> {
    zeroValue: T | undefined;
    add: ((x: T, y: T) => T) | undefined;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
function myAdd(x:number, y:number) { 
    return x + y; 
};
myGenericNumber.add = function(x, y) { 
    return x + y; 
};
console.log(myGenericNumber.add(1,2));

换string玩玩

// 泛型类
class GenericNumber<T> {
    zeroValue: T | undefined;
    add: ((x: T, y: T) => T) | undefined;
}
let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "hi,";
stringNumeric.add = function(x, y) { return x + y; };
console.log(stringNumeric.add(stringNumeric.zeroValue, "test"));

也不是全能的

与接口一样,直接把泛型类型放在类后面,可以帮助我们确认类的所有属性都在使用相同的类型。

注意点??????类有两部分:静态部分和实例部分。 泛型类指的是实例部分的类型,所以类的静态属性不能使用这个泛型类型。

泛型约束

到底有没有长度啊,救命

当我们使用泛型的时候,有这种情况:

我想要打印出传过来的参数的长度为多少

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;
}

相比于操作any所有类型,我们想要限制函数去处理任意带有.length属性的所有类型。 只要传入的类型有这个属性,我们就允许,就是说至少包含这一属性。 为此,我们需要列出对于T的约束要求。

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

现在传入一个数字试试

传入数组:

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

在泛型里使用类类型[]

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

跟在Java中的很像——工厂模式,很是高级

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

高级案例

应用场景:传入这个类,自动创建该类并且返回相应的属性。

class BeeKeeper {
    hasMask: boolean = false;
}
class ZooKeeper {
    nametag:string =  "ZooKeeper.nametag";
}
class Animal {
    numLegs: number = 100;
}
class Bee extends Animal {
    keeper: BeeKeeper = new BeeKeeper();
}
class Lion extends Animal {
    keeper: ZooKeeper = new ZooKeeper();
}
function createInstance<A extends Animal>(c: new () => A): A {
    return new c();
}
console.log(createInstance(Lion).keeper.nametag); 
console.log(createInstance(Bee).keeper.hasMask);

到此这篇关于TypeScript泛型的使用详细介绍的文章就介绍到这了,更多相关TypeScript泛型内容请搜索w3xue以前的文章或继续浏览下面的相关文章希望大家以后多多支持w3xue!

 友情链接: NPS