TypeScript:泛型

什么是泛型

泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。

1 函数泛型

//<T>表示定义模板类型,(arg: T): T 表示参数和返回值类型都是同一个类型,
//具体T是什么类型就实参决定
function a<T>(arg: T): T {
  return arg;
}
let r1 = a(100);//T的类型是number
let r2 = a("小王");//T的类型是string
console.log(r1, r2);

 

匿名函数和箭头函数泛型语法

//匿名函数
let a = function <T>(arg: T): T {
  return arg;
}
//箭头函数
let a = <T>(arg: T):T =>{
  return arg;
}

 

我们来实现一个函数 createArray,它可以创建一个指定长度的数组,同时将每一项都填充一个默认值:

function createArray<T123>(len: number, value: T123): T123[] {
    let arr: any[] = [];
    for (let j: number = 0; j < len; j++) {
        arr.push(value);
    }
    return arr;
}
​
console.log(createArray<string>(5, 'h'));
console.log(createArray(5, 0o700));
​
interface dataType {
    id: number;
    title: string;
    cover: string;
    tags: string[]
}
​
console.log(createArray<dataType>(5, {
    id: 23,
    title: '今天星期天',
    cover: 'http:///7001.png',
    tags: ['创意', '星期天', '线上']
}));

 


 

2 模板类型可以是多个

function a<T1, T2>(a:string, b:T1, c:T2): T2 {
       return c;
}
a("纟", 100, true)
function a<T1, T2>(a:string, b:T1, c:T2): string {
       return "小王";
}
let a = function<T1, T2>(a:string, b:T1, c:T2): T2 {
       return c;
}
let a = <T1, T2>(a:string, b:T1, c:T2): T2 => {
       return c;
}
function change<T1, T2>(tuple: [T1, T2]): [T2, T1] {
    return  [tuple[1], tuple[0]];
}

 

 

3 泛型的错误

function a(a:T1, b:T2): T1 {
   let r = a + b;//编译报错,因为编译器不知道T1和T2是什么类型,所以不确定是否能进行 + 运算
   return r;
}
let r = a("小王", 100);
console.log(r);
function a(a:T1): T1 {
   return a+100;//编译报错,因为编译器不知道T1是什么类型,所以不确定是否能进行 + 100
}
function loggingIdentity(arg: T): T {
    console.log(arg.length) //编译报错,因为编译器不知道T1是什么类型,所以不确定是否有属性length
    return arg
}

 

4 泛型函数变量

函数的变量类型也可以使用泛型

function identity<T>(arg: T): T {
    return arg;
}
​
interface IM {
  {<x>(arg: x):x}
}
let x:IM = identity;//
​
​
let myIdentity1: <U>(arg:U)=>U = identity;
let myIdentity2: {<U>(arg:U):U} = identity;

 

 

5 泛型函数类型接口

(1)泛型函数类型接口1

function identity<T>(arg: T): T {
    console.log("identity")
    return arg;
}
//泛型函数类型接口1, 其中的T可以改为其它自定我,不必与函数中T相同
interface GenericIdentityFn1 {
    <T>(arg: T): T;
}
//值也必须是有泛型的函数
let myIdentity: GenericIdentityFn1 = identity;
//<string>决定了参数只能使用string
myIdentity<string>("sddssd"); 
//<number>决定了参数只能使用number
myIdentity<number>(234); 

 

(2)泛型类函数型接口2

//泛型类函数型接口2
interface GenericIdentityFn2<T> {
    (arg: T): T;
}
//<number>决定了参数只能使用number
let myIdentity2: GenericIdentityFn2<number> = identity;
myIdentity2(234); 
​
//<string>决定了参数只能使用string
let myIdentity3: GenericIdentityFn2<string> = identity;
myIdentity3("小王"); 
console.log(r);

 

6 泛型类1

(1)新new出对象是{ },其中无成员

(2)对象中只能全部或部分实现类中声明好的成员,不能增加其它成员

class GenericNumber<T> {
    zeroValue: T;
    add: (x: T, y: T) => T;
}
let myGenericNumber = new GenericNumber<number>();
myGenericNumber.zeroValue = 0;
myGenericNumber.add = function(x, y) { return x + y; };
// 或 yGenericNumber.add = function(x:number, y:number) { return x + y; };
let r = myGenericNumber.add(1, 22);
console.log(r);
//myGenericNumber.sex = ‘男’; 错,因为类中没有声明sex
​
​
let stringNumeric = new GenericNumber<string>();
stringNumeric.zeroValue = "fds";
stringNumeric.add = function(x, y) { return x + y; };
let r2 = stringNumeric.add("1", "22");
console.log(r2);

 

7 泛型类2

new出的对象已经具体类中的成员

class A<T> {
    a:T;
      show(a:T, b:T) {
                console.log(a, b);
      }
}
let a1 = new A<string>();
a1.show("fsd","fsd")
​
let a2 = new A<number>();
a2.show(123,444)
​
//下面代码也不会报错,T不生效
let a3 = new A();
a3.show(444,"555");

 

 

8 泛型约束

在函数内部使用泛型变量的时候,由于事先不知道它是哪种类型,所以不能随意的操作它的属性或方法.

<T extends IA> 要求T类型数据至少有IA中声明的成员

interface IA {
    length: number;
}
//要求T类型数据有length属性
function m1<T extends IA>(arg: T): T {
    console.log(arg.length);   
    return arg;
}
let r;
r = m1("小王");//"小王"有length属性
console.log(r);//2
​
​
r = m1({length: 10, value: 3});//{length: 10, value: 3}有length属性
console.log(r);//10
interface IA {
    length: number;
      value: number;
}
//要求T有属性length和value
function m1<T extends IA>(arg: T): T {
    console.log(arg.length, arg.value);   
    return arg;
}
m1({length: 10, value: 3, age:30});

 

以上泛型约束示例仅仅是函数中使用,实计上也可以使用于其它使用泛型的地方,看看下面的示例吧

class Person {
    name:string;
    sex:string;
    constructor(name, sex) {
        this.name = name;
        this.sex = sex;
    }
    show() {
        console.log(this.name, this.sex);
    }
}
​
class Stu<T> {
    show(p:T) {
        console.log(p);
        p.show();//有错
    }
}
let stu = new Stu<Person>();
let p = new Person("小王", '男');
stu.show(p);

 

以下示例有一个问题,p.show()会出错, 如果改为下面的泛型约束就可以解决该类问题

class Person {
    name:string;
    sex:string;
    constructor(name, sex) {
        this.name = name;
        this.sex = sex;
    }
    show() {
        console.log(this.name, this.sex);
    }
}
​
class Stu<T extends Person> {
    show(p:T) {
        console.log(p);
        p.show();
    }
}
let stu = new Stu<Person>();
stu.show()
let p = new Person("小王", '男');
stu.show(p);

 

多个类型参数之间也可以互相约束

function copyObj<T extends U, U>(target: T, source: U): T {
    for (const k in source) {
        target[k] = (<T>source)[k];
    }
    return target;
}
let x123 = {a:1, b:2, c:3};
const source1 = {a:1, b:2, c:3};
​
copyObj(x123, source1);
console.log(x123);

 

9 泛型参数的默认类型

我们可以为泛型中的类型参数指定默认类型。当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推测出时,这个默认类型就会起作用。

function createArray<T = string>(length: number, value: T): Array<T> {
    let result: T[] = [];
    for (let i = 0; i < length; i++) {
        result[i] = value;
    }
    return result;
}

 

 
posted on 2022-09-26 20:00  香香鲲  阅读(137)  评论(0编辑  收藏  举报