Typescript泛型及应用

1. 为什么要使用泛型

使用泛型来创建可重用的组件,使得一个组件可以支持多种类型的数据,这样用户就可以以自己的数据类型来使用组件。

//泛型的基础语法
function func <T, U>(value: T, message: U) : T {
  console.log(message);
  return value;
}

console.log(func<Number, string>(68, "小潘"));
//也可以省略尖括号里面的声明类型,编译器能自动识别

2. 在函数中使用泛型

  • 分配泛型参数
    function identity<T>(value: T): T {
      return value;
    }
    
    const result = identity(123);
  • 直接传递类型参数
    type ProgrammingLanguage = {
      name: string;
    };
    function identity<T>(value: T): T {
      return value;
    }
    const result = identity<ProgrammingLanguage>({ name: "TypeScript" });
type User = {
  name: string;
}
async function fetchApi<ResultType>(path: string): Promise<ResultType> {
  const response = await fetch(`https://example.com/api${path}`);
  return response.json();
}
const data = await fetchApi<User[]>('/users')
//注:此例中,泛型ResultType并没有在参数列表中使用,也没有在TypeScript能够推断其值的其他地方使用,这意味着在调用函数时,必须显式地传递此泛型的类型
  • 默认类型参数 

在上述的例子中,调用代码时必须提供类型参数。如果调用代码不包含泛型类型参数,则 ResultType 将推断为 unknow,这时可以为泛型类型参数添加默认类型。通过在泛型类型参数后面添加 = DefaultType 来完成

async function fetchApi<ResultType = Record<string, any>>(path: string): Promise<ResultType> {
  const response = await fetch(`https://example.com/api${path}`);
  return response.json();
}
const data = await fetchApi('/users')
console.log(data.a)
//注:此处的Record<string, any>仅仅表示具有string类型的键和any类型的值的对象
  • 参数类型约束
    //此函数用于接受任何对象并返回另一个对象,其key 值与原始对象相同,但所有值都转换为字符串(映射类型)
    function stringifyObjectKeyValues<T extends Record<string, any>>(obj: T) {
      return Object.keys(obj).reduce((acc, key) =>  ({
        ...acc,
        [key]: JSON.stringify(obj[key])
      }), {} as { [K in keyof T]: string })
    }

        常用场景一:确保属性存在

function identity<T>(arg: T): T {
  console.log(arg.length); // Error
  return arg;
}
//编译器将不会知道 T 确实含有 length 属性,尤其是在可以将任何类型赋给类型变量 T 的情况下
//应该修改如下
interface Length {
  length: number;
}

function identity<T extends Length>(arg: T): T {
  console.log(arg.length); // 可以获取length属性
  return arg;
}

        常用场景二: 检查对象上的某个键是否存在

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

通过 keyof 操作符,我们就可以获取指定类型的所有键,之后我们就可以结合 extends 约束,即限制输入的属性名包含在 keyof 返回的联合类型中 

3. 在接口、类和类型中使用泛型

  •  接口和类中的泛型
    interface MyInterface<T> {
      field: T
    }
    
    class MyClass<T> {
      field: T
      constructor(field: T) {
        this.field = field
      }
    }
  • 自定义类型中的泛型
    type MyIdentityType<T> = T
    type B = MyIdentityType<number>
    //在这种情况下,B的类型就是number

    4. 使用泛型创建映射类型 

使用 TypeScript 时,有时需要创建一个与另一种类型具有相同结构的类型,即有相同的属性,但属性的类型不同。这时可以使用映射类型可以重用初始类型并减少重复代码。这种结构称为映射类型并依赖于泛型

type BooleanFields<T> = {
  [K in keyof T]: boolean;
};
type User = {
  email: string;
  name: string;
}
type UserFetchOptions = BooleanFields<User>;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值