TypeScript 泛型的理解及泛型工具的使用

泛型

泛型作用非常广泛 ,它有着很多的应用场景

  1. 泛型接口
  2. 泛型类
  3. 泛型约束
  4. 泛型的默认参数
  5. 泛型的条件类型
  6. 泛型工具类
  7. 使用泛型创建对象
1. 什么是泛型
  • 泛型(Generics)是指在定义函数、接口或类的时候,不预先指定具体的类型,而在使用的时候再指定类型的一种特性。
// 泛型可定义多个 在这定义两个泛型T,U
function identity<T, U>(value: T, message: U): T {
 console.log(message);
 return value;
}

console.log(identity < Number, string > (68, "Semlinker"));
// console.log(identity(68, "Semlinker")); 简写 类型可根据参数类型自动检测  不用传递类型

我们在声明identity函数名后<>里声明两个泛型T,U 。在不调用该函数时我们不知道T,U的类型。只有我们在调用 identity <Number,string> (68, "Semlinker") 时,在<>中传递Number,string 时才知道 T的类型时Number, U的类型时string。使用泛型时你就把它看成参数传递!!
当然在你调用函数时也可以不向泛型传递类型,ts 会自动帮你检测你的参数类型!!

2. 泛型接口
  • 函数使用时可以省略不传递类型,但接口得传
interface Identities<V, M> {
 value: V;
 message: M;
}

let a: Identities = {}; // 报错 缺少参数
let a: Identities<number, string> = {
    value:2
    message:"7777"
};
// 接口泛型接口可以作用 很多地方 在这例子比较简单 但理解都是一样的
3. 泛型类
  • 在类中使用泛型 和函数中也是一样的
class A<T> {
 // T 接受类型是string
 value: T; // 限制value 的类型string
 constructor(value: T) {
  // 限制了类A接受的参数得是一个string类型
  this.value = value;
 }

 getIdentity(): T {
  // 限制返回值是string
  return this.value;
 }
}

let newA = new A("777"); // ts自动检测 泛型 参数是string 省略<string>不写
// let newA = new A<string>("777");
4. 泛型约束
  • 有时候,我们希望 参数 上存在某些属性
// arg类型是T ,T的类型是根据identity传递参数时的类型,
function identity<T>(arg: T): T {
 console.log(arg.length); // Error 但这个参数可能是一个数字 数字是不存在length属性
 return arg;
}

使用接口来进行 泛型约束 来限制使用该函数时传递的参数必须有 length 属性

interface Length {
  length: number;
}

// extends 继承 在这可理解为T继承了 Length 接口的类型限制 ,多了一种条件
function identity<T extends Length>(arg: T): T {
  console.log(arg.length); // 可以获取length属性
  return arg;
}
// 在我们调用该函数时传入的参数就必须要含有length属性 不然会报错提示
4.2 keyof 操作符
  • keyof 操作符返回某个类型的所有键 ,是一个联合类型
interface Person {
  name: string;
  age: number;
  location: string;
}

type K1 = keyof Person; // "name" | "age" | "location"

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

let tsInfo = {
     name: "Typescript",
     age: 7,
}
// k 继承了 keyof T 的类型, 而 keyof T 返回联合类型 name | age 存放在 T 里。
// 这就限制了我们第二个参数只能是tsInfo的键名
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}

getProperty(tsInfo, 'name'); // OK Typescript
getProperty(tsInfo, 'n_ame'); // Error
5、泛型参数默认类型
  • 泛型的参数的默认值就和函数的参数是一样的。 当使用泛型时没有在代码中直接指定类型参数,从实际值参数中也无法推断出类型时,这个默认类型就会起作用。
interface A<T = string> {
 name: T;
}

const strA: A = { name: "Semlinker" }; // 没有指定类型 那么就是默认类型string
const numB: A<number> = { name: 101 }; // 指定了类型number
6. 类型兼容
  • 类型兼容性用于确定一个类型是否能赋值给其他类型,TypeScript 结构化类型系统的基本规则是,如果 x 要兼容 y,那么 y 至少具有与 x 相同的属性。
interface INamed {
 name: string;
}

let x: INamed = { name: "77" };
let y = {
 name: "funlee",
 age: 18,
};

x = y; // ok
y = x; // error x想要赋值给y 就要包含y中所有的值
7. 泛型条件类型
  • 条件类型,使得我们可以根据某些条件得到不同的类型,
T extends U ? X : Y

结合类型兼容的知识来理解 ,若 T 能赋值给 U,则类型为 X,否则类型为 Y 有点类似于 JavaScript 中的三元条件运算符。在这里 extends 你理解为 包含 会更明白点。

interface Water {
    water: string
}

interface Fish {
    water: string
    fish: string,
}

interface Sky {
    sky: string
}

// 当 T 接受传递的类型 它会看看该类型是否包含着 Fish , true 返回 warter, false返回Sky
type Condition<T> = T  extends Fish  ? Water : Sky;

let condition1: Condition<Water> = { water: '水' };
// error  T 赋值不了 Fish 缺少 fish 字段 ,所以此时的类型是 Sky
let condition2: Condition<Fish> = { water: '水' };
// ok     T 能够赋值 Fish

6.2 infer 关键字
  • infer 的作用一言蔽之:抽取泛型参数
// infer R 表示声明一个变量 R 并将抽取(推断)的类型保存在里面
type Type<T> = T extends Array<infer R> ? R : T;
type TypeA = Type<string[]>  // string
type TrypB = Type<never>     // nerver

我们来分析一下以上代码:

  1. 首先判断泛型 T 是否数组类型;
  2. 如果是数组类型,将数组成员类型 R 抽出来并返回;
  3. 如果不是数组类型,则原样返回 T;

案例可能比较简单但是就是这个理,大家可以在网上寻找更多例子

7. 泛型工具
7.1 Partial
  • 将传入的属性变为可选项
interface IPeople {
    title: string;
    name: string;
}

const people: IPeople = {
    title: 'Delete inactive users'
}; 
// error 缺少了name属性

const people: Partial<IPeople> = {
    title: 'Delete inactive users'
};
// ok 通过 Partial 将接口 Ipeople 的属性变为可选类型

7.2 Record<K, T>
  • 类型参数 K 提供了对象属性名联合类型,类型参数T提供了对象属性的类型
interface Person {
  name: string;
}

// 传递 x y,此时的 x y 的类型都是Person
type Peoples = Record<"x" | "y", Person>; // 等于下面写法
// Peoples={
//   x:Person,
//   Y:Person,
// }
const P: Peoples = {
    x: {
        name: '张三'
    },
    y: {
        name: '李四'
    }
}
7.3 Readonly
  • 把传入的类型变为只读状态
interface Person {
  name: string;
  age: number;
}

// 设置值后就不可以再次更改了 
const p: Readonly<Person> = {
    name: '张三',
    age: 22
}

p.name = '李四'; // 无法分配到 "name" ,因为它是只读属性
7.4 Required
  • 把传入的类型变为必填状态
interface Person {
    name?: string;
    age?: number;
}

const p: Required<Person> = {
    name: '张三',
    age: 22
}
7.5 Exclude<T, U>
  • 该工具类型能够从类型T中剔除所有可以赋值给类型U的类型
type T0 = Exclude<"a" | "b" | "c", "a">;
// 相当于 type T0 = "b" | "c"

type T1 = Exclude<"a" | "b" | "c", "a" | "b">;
// 相当于 type T1 = "c"

type T2 = Exclude<string | number | (() => void), Function>;
// 相当于 type T2 = string | number	
7.6 Extract<T, U>
  • 表示 如 T 是、能够赋值给 U 返回 never 类型,如不是返回 T 类型。 当 T 为联合类型的时候,它会自动分发条件。
type T0 = Extract<'a' | 'b' | 'c', 'a' | 'f'>;
// 相当于 type T0 = 'a';

type T1 = Extract<string | (() => void), Function>;
// 相当于 type T1 = () => void;

type T2 = Extract<string | number, boolean>;
// 因为没有交集,相当于 type T2 = never;

联合类型的话 它会一个个去比较 若能赋值他就会保留 该类型,它和 Exclude 类似。
Exclude 去除相同的
Extract 保留相同的
7.7 Omit<T, K>
  • 在 T 中删除对应的 K
interface IPerson {
    name: string;
    age: number;
}

type TP = Omit<IPerson, 'age'>;

const p: TP = {
    name: '张三',
    age:3,// 报错 没有该类型
}
7.8 ReturnType
  • 该工具类型能够获取函数类型T的返回值类型
// string
type T0 = ReturnType<() => string>;

// { a: string; b: number }
type T1 = ReturnType<() => { a: string; b: number}>;

文章借鉴
知乎一文读懂泛型
掘金泛型工具及实现原理

  • 3
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值