TypeScript学习笔记(10)-泛型

泛型的由来

如果想要创建可处理多种类型而不只是一种类型的组件,该怎么操作? 可以使用  any  类型,但这样就失去了 TypeScript 类型检查系统的功能。

泛型的特点
  • (可复用行性) 泛型是可以在代码库中定义和重复使用的代码模板。

  • (定义时指示性) 它们提供了一种方法,可用于指示函数、类或在调用接口时要使用的类型。

  • (调用时指示性) 可以通过将参数传递给函数的方式来理解,不同之处是使用泛型可以指示组件在被调用时应该使用哪种类型。

使用时机

当代码是满足以下条件的函数或类时,创建泛型函数:

  1. 处理各种数据类型。 在多个位置使用该数据类型。 泛型可以:

  • 在处理类型时提供更大的灵活性。

  • 实现代码重用。

  • 减少使用  any  类型的需要。

示例 :getArray  函数生成  any  类型的项的数组
// 使用any 传入任何参数 并返回任何参数
function getArray(items: any[]): any[] {
    return new Array().concat(items);
}

let numberArray = getArray([5, 10, 15, 20]);
let stringArray = getArray(['Cats', 'Dogs', 'Birds']);
numberArray.push(25);                       // OK
stringArray.push('Rabbits');                // OK
numberArray.push('This is not a number');   // OK
stringArray.push(30);                       // OK
console.log(numberArray);                   // [5, 10, 15, 20, 25, "This is not a number"]
console.log(stringArray);                   // ["Cats", "Dogs", "Birds", "Rabbits", 30]

上面是 any 类型 没有类型检查等 ,如果想要在调用函数时确定数组将包含的值的类型,然后让 TypeScript 对传递给它的值进行类型检查以确保它们属于该类型,该怎么操作? 这时泛型就可以发挥作用了。

泛型函数定义

使用泛型重写  getArray  函数。 现在,它可以接受你在调用函数时指定的任何类型。

function getArray<T>(items: T[]): T[] {
return new Array<T>().concat(items);
}
  • 泛型定义一个或多个“类型变量”来标识要传递给组件的一个或多个类型(用尖括号 (< >) 括起来)

  • 函数中的类型变量称为 <T>。 T  是泛型的常用名称,但可以根据需要对其进行命名。

  • 指定类型变量后,可以使用它来代替参数类型、返回类型或将其置于函数中要添加类型批注的任何其他位置。

调用泛型函数

若要调用函数并向其传递类型,请将  <type>  追加到函数名称。

  • 例如,getArray<number> 表示了
    1. 指示函数仅接受  number  值的数组

    2. 并返回  number  值的数组。

    3. 因为类型已指定为  number,所以 TypeScript 会预期将  number  值传递给函数,如果传递的是其他值,则会引发错误。

  1. 如果在调用函数时省略类型变量,TypeScript 将推断类型。 但是,这仅适用于简单数据。

  2. 如果传入数组或对象,会导致推断任何类型(any)并消除类型检查。

function getArray<T>(items: T[]): T[] {
return new Array<T>().concat(items);
}

let numberArray = getArray<number>([5, 10, 15, 20]);
numberArray.push(25); // OK
numberArray.push('This is not a number'); // 类型“string”的参数不能赋给类型“number”的参数。

let stringArray = getArray<string>(['Cats', 'Dogs', 'Birds']);
stringArray.push('Rabbits'); // OK
stringArray.push(30); // 类型“number”的参数不能赋给类型“string”的参数。
使用多个类型变量

泛型组件中并不是只能使用单个类型变量。

// 使用多个变量
function person11<T, U>(name: T, age: U): U {
    console.log(age);
    return age
}

let returnNumber = person11<string, number>('王大可1', 18);
let returnString = person11<string, string>('王大可2', '28');
let returnBoolean = person11<string, boolean>('王大可3', true);

returnNumber = returnNumber * 100;   // OK
returnString = returnString * 100;   // 错误 算术运算左侧必须是 "any"、"number"、"bigint" 或枚举类型
returnBoolean = returnBoolean * 100; // 错误 算术运算左侧必须是 "any"、"number"、"bigint" 或枚举类型
使用泛型类型的方法和属性
  1. 上面的T,U 非常宽泛,导致无法再函数内部无法使用变量。

  2. 使用类型变量创建泛型组件时,只能使用每种类型可用的对象的属性和方法。

  3. 防止参数不兼容的操作引起错误。

function person11<T, U>(name: T, age: U): U {
    let result: T = name + name;    // Error 运算符“+”不能应用于类型“T”和“T”
    console.log(age);
    return age
}

使用泛型约束来限制类型

为了解决上面的问题故而限制类型变量的变量范围

  1. 自定义 type 声明为元组 然后使用自定义类型 extend 类型变量

  2. 类型限制为另一个对象的属性

//自定义 type 声明为元组
type LimitTypes = string | number;

function person11<T extends LimitTypes, U>(name: T, age: U): U {
    let result: T = name + name;    // Error 运算符“+”不能应用于类型“T”和“T”
    console.log(age);
    return age
}

// 类型限制为另一个对象的属性
function getPets<T, K extends keyof T>(pet: T, key: K) {
    return pet[key];
}

let pets1 = { cats: 4, dogs: 3, parrots: 1, fish: 6 };
let pets2 = { 1: "cats", 2: "dogs", 3: "parrots", 4: "fish" }

console.log(getPets(pets1, "fish"));  // 输出 6
console.log(getPets(pets2, "3"));     // Error 类型“"3"”的参数不能赋给类型“4 | 3 | 1 | 2”的参数。
对泛型使用类型保护

在上面的例子中 虽然限定了泛型类型 LimitTypes 但是还是不能执行操作,这时可以使用类型保护来处理逻辑。

function person11<T, U extends LimitTypes>(name: T, age: U) {
    // let result: T = name + name;    // Error 运算符“+”不能应用于类型“T”和“T”

    let result: LimitTypes = '';
    let typeValue: string = typeof age;

    if (typeof age === 'number') {           // Is it a number?
        result = age + age;                // OK
    } else if (typeof age === 'string') {    // Is it a string?
        result = age + age;                // OK
    }
    console.log(`年龄是 ${age} 类型是 ${typeValue} 返回是 ${result}`);

    console.log(age);
    return result
}

let returnNumber = person11<string, number>('王大可1', 18);
let returnString = person11<string, string>('王大可2', '28');
  • 只能使用 typeof 类型保护来检查基元类型 string、number、bigint、function、boolean、symbol、object 和未定义类型。

  • 若要检查类的类型,请使用 instanceof 类型保护。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值