Typescript中泛型的介绍及用法

目录

  • 泛型
    • 泛型变量
    • 泛型类型
    • 泛型接口
    • 泛型类
    • 泛型约束
      • 泛型约束中使用类型参数

泛型

   泛型 是指宽泛的类型,通常用于类和函数,表示在使用的时候再指明类型,泛型就是解决类接口方法的复用性、以及对不特定数据类型的支持。泛型不仅可以让我们的代码变得更加健壮,还能让我们的代码在变得健壮的同时保持灵活性和可重用性。用法是通过用 来表示,放在参数的前面,T帮助我们捕获用户传入的类型。

function abc(value: number): number { return value; }  //输入和输出都是number类型
function abc1(value: any): any { return value; }  //输入和输出都是any类型,这样就丢失了一些信息:传入的类型与返回的类型应该是相同的
//给abc2添加了类型变量T,之后就可以使用这个类型,然后再次使用T当做返回值类型
function abc2<T>(value: T): T { return value; }  //这就是泛型,可以适用于多个类型,同时输入的参数类型和返回值的类型是相同的

泛型变量

  使用泛型创建像以上类似的泛型函数时,编译器要求我们在函数体内必须正确的使用这个通用的类型。 换句话说,我们必须把这些参数当做是任意或所有类型。例如以下示例:

function ab<T>(arg: T): T {
    console.log(arg.length);  //报错,因为T是任意或所有类型,而不是所有类型(如number和boolean)都有length属性
    return arg;
}
//因此,我们可以进行些许更改:
function ab1<T>(arg: T[]): T[] {  //泛型函数接收一个元素类型为T的数组
    console.log(arg.length);  //数组有属性length
    return arg;
}

这可以让我们把泛型变量T当做类型的一部分使用,而不是整个类型,增加了灵活性。

泛型类型

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

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

//还可以使用带有调用签名的对象字面量来定义泛型函数
function identity1<T>(arg: T): T {
    return arg;
}
let myIdentity: {<T>(arg: T): T} = identity1;

泛型接口

  将泛型与接口结合起来使用,可以大大简化我们的代码,增加我们的代码可读性。

//泛型接口的不同写法一
interface as {
    <T>(val: T): T
}
function ts<T>(val: T): T {
    return val;
}
let myVal:as = ts;
console.log(myVal('阿巴阿巴'));

//泛型接口的不同写法二:把泛型参数当作整个接口的一个参数,这样我们就能清楚的知道使用的具体是哪个泛型类型,接口里的其它成员也能知道这个参数的类型
interface as1<T> {
    (val: T): T
}
function ts1<T>(val: T): T {
    return val;
}
let myVal1:as1<string> = ts1;
console.log(myVal1('鞠婧祎'));

泛型类

  泛型类看上去与泛型接口差不多。 泛型类使用 <> 括起泛型类型,跟在类名后面。与接口一样,直接把泛型类型放在类后面,可以帮助我们确认类的所有属性都在使用相同的类型。

class List<T> {
    public list: T[] = [];
    add(a: T): void {
        this.list.push(a);
    }
}
let s = new List<string>();
s.list = ['小红','小美','小兰'];
s.add('111');
console.log(s);  //List { list: [ '小红', '小美', '小兰', '111' ] }
console.log(s.list);  //[ '小红', '小美', '小兰', '111' ]

class Info<T1,T2> {
    username: T1;
    pwd: T2;
    constructor(username: T1,pwd: T2) {
        this.username = username;
        this.pwd = pwd;
    }
}
let info = new Info<string,string>('八月八','123456');
console.log(info);  //Info { username: '八月八', pwd: '123456' }

注意:泛型类指的是实例部分的类型,所以类的静态属性不能使用这个泛型类型!

泛型约束

  在TypeScript中,我们需要严格的设置各种类型,在使用泛型之后将会变得更加灵活,但同时也将会存在一些问题。因此我们需要对泛型进行约束来解决这些问题。

function ab<T>(arg: T): T {
    console.log(arg.length);  //报错,因为T是任意或所有类型,而不是所有类型(如number和boolean)都有length属性
    return arg;
}

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

interface Length {
    length: number;
}
function ab<T extends Length>(val: T): number {
    return val.length;
}
let a1 = ab('刘亦菲');
let a2 = ab([1,2,3,4]);
let a3 = ab({length: 33});
console.log('a1:', a1);  //3
console.log('a2:', a2);  //4
console.log('a3:', a3);  //33

现在这个泛型函数被定义了约束,因此它不再是适用于任意类型。我们需要传入符合约束类型的值,必须包含必须的属性length,这就是泛型约束。

泛型约束中使用类型参数

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

interface ok {
    [key: string]: any,
}

let getProps = (obj: ok, key: string): number => {
    return obj[key];
}

let x = {'abc':1,b:2,10:3};
let q = getProps(x,'abc');
console.log(q);  //1
let q1 = getProps(x,'d');
console.log(q1);  //undefined

//K代表T中必须有的属性,用T来限制类型参数K
function getProperty<T,K extends keyof T>(obj: T,key: K) {
    return obj[key];
}
let x1 = {'abc':1,b:2,10:3};
let q2 = getProperty(x1,10);
console.log(q2);  //3
let q3 = getProperty(x1,'d');  //报错
console.log(q3); 

你可能感兴趣的:(TypeScript,typescript,javascript,前端)