ts类型体操笔记

ts类型体操笔记

一、infer关键字

1. infer 作用

用于推导泛型参数,并且infer的声明只能出现在extend子语句中

2. infer 用法

infer后面加变量名

例如:获取 Promise 返回的参数

interface User {
  name: string
  age: number
}

type PromiseType = Promise<User>
type GetPromiseType<T> = T extends Promise<infer U> ? U : never
type res = GetPromiseType<PromiseType> // type res = User

// 多层嵌套需要结合递归实现
type PromiseType2 = Promise<Promise<Promise<User>>>
type GetPromiseType2<T> = T extends Promise<infer U> ? GetPromiseType2<U> : T
type res2 = GetPromiseType2<PromiseType2> // type res2 = User

3. infer 协变

infer 协变主要出现在对象的属性中,infer类似解构用法时,返回的是联合类型

返回的是联合类型

let obj = {
  name: 'wifi',
  age: 18
}

type Bar<T> = T extends { name: infer N; age: infer A } ? [N, A] : never
// 发生infer协变就出现在当两个参数变量名相同时,都为U,U就会变为联合类型
type Bar2<T> = T extends { name: infer U; age: infer U } ? U : never

type res2 = Bar2<typeof obj> // type res2 = string | number

4. infer逆变

infer逆变出现在函数的参数上面,返回的是交叉类型

返回的是交叉类型

type Bar<T> = T extends {
    a: (x: infer U) => void;
    b: (x: infer U) => void;
} ? U : 'asd';

type res = Bar<{
    a: (x: number) => void;
    b: (x: string) => void;
}>; // type res = never

type res2 = Bar<{
    a: (x: number) => void;
    b: (x: number) => void;
}>; // type res2 = number

5. infer妙用

例如:提取头部元素

type Arr = ['a', 'b', 'c']

type First<T extends any[]> = T extends [infer One, infer Two, infer Three] ? One : []
type res = First<Arr> // type res = "a"

type First2<T extends any[]> = T extends [infer First, ...any[]] ? First : []
type res2 = First2<Arr> // type res2 = "a"

二、对象

1. 遍历

对于普通对象,一般通过for in遍历。对于类型,使用keyof Object获得它所有的键,再通过in关键字遍历

// js
for (let key in obj) {}

// 类型
[T in keyof Object]

2. 取值

type Object = {
  name: 'wifi',
  age: 20
}
Object['name' | 'age'] // string | number(取的就是后面wifi和20的类型)

示例:

  1. 实现ValueOf
type ValueOf<Object> = Object[keyof Object];

type a = ValueOf<{ name: string; age: 20 }>;

三、元组/数组

1. 类型中的元组

1.1 取值
type Tuple = [string, number]

Tuple[0] // number
Tuple[0 | 1] // string | number
1.2 收集、展开
[...Tuple1, ...Tuple2]
1.3. 长度
Tuple['length']

2. 类型中的数组

type Arr = string[];
Arr[number] // 获取数组的类型

// 另一个小技巧
Tuple[number] // 可以获取元组的所有类型的集合

示例:

  1. 实现Append<Tuple, Element>
type Append<Tuple extends any[], Element> = [...Tuple, Element]

type a = Append<[1,string], true>  // [1, string, true]
  1. 返回长度+1
type Length<Tuple extends any[]> = [any, ...Tuple]['length']

type a = Length<[1, 2]> // 3
  1. 将元组转化为数组
type To<Tuple extends any[]> = Array<Tuple[number]>

type a = To<[1,'hello', true]> // (true | 1 | "hello")[]

四、条件语句if…else…

type isNumber<T> = T extends number ? true : false
type a = isNumber<1> // true
type b = isNumber<'1'> // false

type isNumber2<T, K> = [T, K] extends [number, number] ? true : false

上面两个语句的区别在于extends关键字上。extends关键字最直观的描述是:A extends B就代表A能否赋值给B,也就是A是否是B的子集。

这是extends的第一个作用,当作条件语句,它还有另外一个重要作用,就是做类型约束,但其本质还是判断A是否是B的子集的问题,A是B的子集。

type Fn<T extends boolean> = ...;

Fn<true> // 正确
Fn<1> // 报错

// 在没有约束的默认情况下可以认为是
type Fn<T extends unknown> = ...;

示例:

  1. 判断输入的是否是数组
type isArray<Arr> = Arr extends Array<any> ? true : false;

type a = isArray<[1, 2, 3]>; // true
type b = isArray<1>; // false
```
  1. GetProp<Key, Object>,可能Key不存在于Object,返回undefined
type GetProp<Object, Key> = Key extends keyof Object ? Object[Key] : undefined;

type a = GetProp<{ name: 'wifi'}, 'name'>; // wifi
type b = GetProp<{ name: 'wifi'}, 'age'>; // undefined

五、infer类似解构赋值

1. 使用

type GetName<User> = User extends { name: infer Name } ? Name : never;

infer关键字,它可以为类型变量赋值,把参数的值提取到infer后的类型中,此时Name中保存的就是name字段的值,如果User满足类型约束,则会得到Name并返回,否则返回undefined。

2. infer在元组中的应用也很多:

type getFirst<T> = T extends [infer First, ...infer Rest] ? First : undefined;
type a = getFirst<[1,2,3]>; // 1

type getRest<T> = T extends [infer First, ...infer Rest] ? Rest : undefined;
type a = getRest<[1,2,3]>; // 2, 3

3. 函数的参数也可以通过infer赋值:

type Params<F> = F extends (...params: infer P) => any ? P : undefined;
type a = Params<(a: string, b: number) => void>; // [a: string, b: number]

六、赋值

如果不想通过解构赋值,而是直接把一个变量赋值给另一个呢?

type Fn<T> = T extends infer P;

使用A extends infer B的意思是把A的值赋给B,并且这两个变量可以在分支语句中使用。

七、循环

在类型中,循环只能通过递归完成

// js中实现递归
const Fn = (a, b) => {
    flag === true ? Fn(c, d) : e;
}

// ts实现
type Fn<A, B> = Flag extends true ? Fn<C, D> : E;

条件语句换成了extends关键字

例如:遍历元组,不论遍历数组还是元组,都可以通过循环完成。

type Loop<List> = List extends [infer First, ...infer Rest] ? Loop<Rest> : {};

本质就是:每次只处理第一个元素,然后不断递归剩余元素

示例:

  1. 实现map
type Maps<List> = List extends [infer First, ...infer Rest] ? [{ name: First }, ...Maps<Rest>] : []

type a = Maps<['a', 'b', 'c']>
/**
 * [{
        name: "a";
    }, {
        name: "b";
    }, {
        name: "c";
    }]
 */
  1. 实现filter
type FilterNumber<List> = List extends [infer First, ...infer Rest]
  ? First extends number
    ? [First, ...FilterNumber<Rest>]
    : FilterNumber<Rest>
  : []

type a = FilterNumber<[1, 'a', 2, 'b', 3, true]> // [1, 2, 3]
  1. Take<Tuple, N, Output>

例如:

Take<[1,2,3], 2> => [1,2] (返回数组前两个)

Take<[1,2], 5> => [1,2] (不够就都返回)

type Take<Tuple, N, Output extends any[] = []> = Tuple extends [
  infer First,
  ...infer Rest
]
  ? Output['length'] extends N
    ? Output // 已经等于数组长度,返回结果
    : Take<Rest, N, [...Output, First]> // 将剩余的元素继续递归
  : Output // []

type a = Take<[1, 2, 3], 2> // [1, 2]

八、字符串

1. 模版字符串与extends/infer

type str = 'hello'

// 获取字符串的第一个字符
type getFirst<T> = T extends `${infer First}${infer R}` ? First : never
// 获取字符串除了第一个字符
type getRest<T> = T extends `${infer First}${infer R}` ? R : never
type res = getFirst<str> // type res = "h"
type res2 = getRest<str> // type res2 = "ello"

// 以y作为间隔,获取y前和y后字符串
type str2<T> = T extends `${infer Front}y${infer Last}` ? Front : never
type str3<T> = T extends `${infer Front}y${infer Last}` ? Last : never
type res3 = str2<'beyond'> // type res3 = "be"
type res4 = str3<'beyond'> // type res4 = "ond"

示例:

  1. 判断字符串开头
type startWith<
  str extends string,
  T extends string
> = str extends `${T}${infer rest}` ? true : false
type res = startWith<'abc', 'a'> // type res = true
  1. 首字母大写
type UppercaseFirst<str extends string> = str extends `${infer F}${infer R}`
  ? `${Uppercase<F>}${R}`
  : str

type res = UppercaseFirst<'hello'> // type res = "Hello"
  1. 文本替换
type ReplaceOne<
  str extends string,
  from extends string,
  to extends string
> = str extends `${infer Front}${from}${infer Rest}`
  ? `${Front}${to}${Rest}`
  : str

type res = ReplaceOne<'hello world', ' ', '!'> // type res = "hello!world"
  1. 键值对转索引类型

例如:a=1 => {a:1}

type ConverStrToRecord<str extends string> =
  str extends `${infer Key}=${infer Value}` ? { [k in Key]: Value } : never

/**
 * type res = {
      a: "1";
  }
 */
type res = ConverStrToRecord<'a=1'>
  1. 对象索引转大写
interface B {
  aaa: 1;
  bbb: 2;
}
type UpperKeys = {
  [K in keyof B as Uppercase<K>]: B[K]
}

type res = UpperKeys // { AAA: 1; BBB: 2; }
  1. 只获取函数的索引
interface B {
  aaa: 1;
  bbb: 2;
  fun: () => true
}
type UpperKeys = {
  [K in keyof B as (B[K] extends Function ? K : never)]: B[K]
}

/**
 * type res = {
      fun: () => true;
    }
 */
type res = UpperKeys
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值