10TypeScript泛型进阶

65 阅读3分钟

一. 类型映射, [K in Keyof T]

  • readonly例子
type Person = { name: string; age: number };

// [K in keyof T] // keyof T 表示 "name" | "age" ,
// K in keyof Person,这实际上是在遍历联合类型 "name" | "age" 中的每一个
// 依次就是"name", "age"
type myReadonly<T> = {
  readonly [K in keyof T]: T[K];
};
type X2 = myReadonly<Person>;
type X1 = Readonly<Person>;
  • partial例子
type Person = { id: number; name: string; age: number };
type X2 = Partial<Person>;
// 自己手动实现一个Partial
type myPartial<T> = {
  [K in keyof T]?: T[K];
};
type X3 = myPartial<Person>;

  • 必填requried,自己写的版本
type Person = { id?: number; name: string; age: number };
type myRequired<T> = {
  [K in keyof T]-?: T[K];
};
// 此时p的类型就是全是必填属性的Person
type P = myRequired<Person>;
  • record例子
type myRecord<Key extends string | number | symbol, Value> = {
  [k in Key]: Value;
};

type X4 = myRecord<string, number>;

二. :in的区别

type Y = {
  [k: string]: number;
};

type Z = {
  [k in string]: number;
};

type YK = keyof Y; // 这是String | Number,这样是为了兼容js中对象key为数字的情况
type ZK = keyof Z; // 这样得到的是String

这里的 : 用于指定对象类型的属性类型,也就是在对象中的每个属性名 k 对应的属性值的类型是 number。这种方式定义了一个类型,该类型表示具有字符串键的对象,其中每个键都对应一个数字值。这通常用于描述对象的结构,其中键是字符串,值是相同类型的数字。

这里的 in 是映射类型的语法。它用于定义一个映射类型,其中字符串类型 string 中的每个键 k 被映射到数字类型 number。这种方式通常用于创建从一个类型到另一个类型的映射关系,而不是描述特定对象的结构。

总之,主要区别在于 : 用于定义对象属性的类型,而 in 用于创建类型映射。这两种方式具有不同的用途和语法,虽然它们都涉及到键和类型之间的关系。

三. 三星的类型体操

  • Exclude的实现,myExclude

1.JPG

// 集合A中有1,2,3,集合B中有1,2 想要实现A中去掉B的元素剩下3要怎么做
type X5 = Exclude<1 | 2 | 3, 1 | 2>;

type myExclude<A, B> = A extends B ? never : A;
type X6 = myExclude<1 | 2 | 3, 1 | 2>;
// 可以这样理解
// type X6 =
// | 1 extends 1 | 2 ? never : 1 // never
// | 2 extends 1 | 2 ? never : 2 // never
// | 3 extends 1 | 2 ? never : 3 // 3
// type X6 = never | never | 3
  • extract的实现
// extract, 接收1|2|3 取出2|4,没有就不管,有点像取交集,其实就是上衣题的反操作
// 重点是画图
type myExtract<A, B> = A extends B ?  A : never
type X6 = myExtract<1|2|3, 2|4> // 2 
  • omit的实现
// omit忽略对象的key
type Person = {id: number, name: string, age: number}      
// K2 in keyof T as never就是全删除,as有点像赋值,会覆盖
// 这属于正向操作
type myOmit<T, Key> = { 
    [K2 in keyof T as (K2 extends Key ? never : K2)]: T 
}
type X7 = myOmit<Person, 'name' | 'age'>
  • pick的实现,以及另一种方式实现omit
type myPick<T, Key extends keyof T> = { 
    [K2 in Key]: T[K2]
}
type X8 = myPick<Person, 'name' | 'age'>
  
// 当我们有pick,我们就可以更简单的去写omit,
// 通过Exclude集合的减法,从所有的里面减去不要的,剩下的就是要的
// 这属于反向的思路
type myOmit2<T, Key extends keyof T> = myPick<T, Exclude<keyof T, Key>>
type X9 = myOmit2<Person, 'name' | 'age'>
  • 把对象属性都变成可写
type Person = {
  readonly id: number;
  readonly name: string;
  readonly age: number;
};

type Mutable<T> = {
  -readonly [K in keyof T]: T[K];
};

type X9 = Mutable<Person>;

四. 总结

  • in keyof经常搭配用,其它的见图
  • as只在readonly的时候用到

222.JPG

五. 最后推荐一个练习项目