美文网首页
TS 类型体操合集

TS 类型体操合集

作者: l1n3x | 来源:发表于2022-04-18 20:53 被阅读0次

基本姿势

keyof

keyof 返回一个类型的所有 key 的联合类型:

type KEYS = keyof {
    a: string,
    b: number
} // a|b

类型索引

类型索引可以通过 key 来获取对应 value 的类型:

type Value = {a: string, b: number}['a'] // string

特别的,使用 array[number] 可以获取数组/元组中所有值类型的联合类型:

type Values = ['a', 'b', 'c'][number] // 'a'|'b'|'c'

in 操作符与类型映射

in 操作符有点类似于值操作中的 for in 操作,可以遍历联合类型,结合类型索引可以从一个类型中生成一个新的类型:

// 从 T 中 pick 出一个或多个 key 组成新的类型
type MyPick<T, S extends keyof T> = {
    [R in S] : T[R]
} 
type PartType = MyPick<{a: string, b: number, c: number}, 'a'|'b'> // {a: string, b: number}

同样,数组类型也可以遍历,R in keyof T 的结果为数组的下标:

type ArrayIndex<T extends any[]> = {
    [R in keyof T]: R
}
// 的到一个数组下标组成的新数组类型
type Indexes = ArrayIndex<['a', 'b', 'c']> // ['0', '1', '2']

extends

extends 类型于值运算符中的三元表达式:

S extends T ? K : V

若 S 兼容 T 则返回类型 K 否则返回类型 V,例如:

type Whether = "a" extends "a"|"b" ? true : false // true
type Whether2 = {a: string} extends {b: string} ? true : false // false

extends 中有一个重要的概念为类型分发,例如:

type Filter<T, S> = T extends S ? never : T
type X = Filter<'a'|'b'|'c', 'c'> // 'a'|'b'

从直观上来看 Filter 的作用是计算 'a'|'b'|'c' extends 'c' 这个表达式显然不成立,应该返回 never。但是实际上返回了 'a'|'b'。这是由于当 extends 需要检测的类型为泛型联合类型时,会将联合类型中的每一个类型分别进行检测。因此 'a'|'b'|'c' extends 'c' 实际等价于:

'a' extends 'c' ? never : T | 'b' extends 'c' ? never : T | 'c' extends 'c' ? never : T 
  = 'a' | 'b' | never 
  = 'a' | 'b' 

这里也包含了另外一个知识点,xxx|never=xxx。可以将联合类型与 extends 结合使用达到循环的效果。如果要阻止类型分发,只需要在外面套一个数组即可:

type Filter<T, S> = T extends S ? never : T
type X = Filter<'a'|'b'|'c', 'c'> // 'a'|'b'

type Filter<T, S> = [T] extends [S] ? never : T
type X = Filter<'a'|'b'|'c', 'c'> // never

如果很多时候我们既需要类型分发后的类型,还需要类型分发前的联合类型。例如如果我们判断一个类型是否为联合类型,那么可以:

type IsUnion<T> = T extends T ? [Exclude<T, T>] extends [never] ? false: true : never

即如果一个类型是联合类型,那么 execlude 掉一个其中的类型后其类型不会为 never。否则就为 never。但是在 Exclude 中出现了两 T 这明显是不行的。因此可以利用 TS 的默认类型:

type IsUnion<T, R=T> = T extends any ? [Exclude<R, T>] extends [never] ? false: true : never

这种方法可以用在既需要分发后的类型也需要原始类型的情况。

此外,extends 还有另一个需要注意的地方,泛型变量无法直接与 never 比较,需要套一个数组,例如:

type IsNever<T> = T extends never ? true: false
type Y = IsNever<never> // never

type IsNever<T> = [T] extends [never] ? true: false
type Y = IsNever<never> // true

infer

infer 可以类比到值元算的类型匹配,在类型体操中有非常多的应用。例如对于 scala:

a match {
case Success(val) => val
case _ => None
}

当 a 值为 Success() 类型时提取其中的 val。利用 infer 也可以达到相同的效果:

type ExtractType<T> = T extends {a: infer R} ? R : never // 匹配成功返回 R 否则返回 never
ExtractType<{a: {b: string}}> // {b: string}

可以看出先定义了一个模板 {a: infer R} 然后用于匹配类型 {a: {b: string}},这时就可以得到 R = {b: string}。目前 infer 出来的类型仅能应用到 extends 的成功分支
infer 也可以用匹配字面量的类型,例如:

type Startswith<T, S extends string> = T extends `${S}${infer R}` ? true : false
Startswith<"hello world", "hello"> // true
Startswith<"hello world", "world"> // false

type Strip<T, S extends string> = T extends `${S}${infer R}` ? R : T
type Y1 = Strip<"hello world", "hello "> // world
type Y2 = Strip<"hello world", "world"> // hello world

数组/元组类型

数组类型可以使用 ... 操作符进行展开:

type Add<S extends any[], R> = [...S, R]
type Y3 = Add<[1, 2, 3], 4> // [1, 2, 3, 4]

元组表示不可修改的数组,可以使用 as const 将数组转换为元组。

const array1 = [1, 2, 3, 4]
type X1 = typeof array1 // number[]
type X2 = X1[number] // number

const array2 = [1, 2, 3, 4] as const
type Y1 = typeof array2 // readonly [1, 2, 3, 4]
type Y2 = Y1[number] // 1|2|3|4

递归类型

在 typescript 类型操作符中不存在循环表达式,但是可以使用递归来进行循环操作,例如:

type TrimLeft<T extends string> = T extends ` ${infer R}`? TrimLeft<R>: T
type Y7 = TrimLeft<'  Hello World  '> // Hello World  

type Concat<S extends any[]> = S extends [infer R, ...infer Y] ? `${R & string}${Concat<Y>}` : ''
type Y6 = Concat<['1', '2', '3']>

type Join<S extends any[], T extends string> = S extends [infer R, ...infer Y] ? 
                                                (Y['length'] extends 0 ? R: `${R & string}${T}${Join<Y, T>}`)  : ''
                                               
type Y4 = Join<['1', '2', '3'], '-'> // '1-2-3'


type Flatten<S extends any[]> = S extends [infer R, ...infer Y] ? 
    (R extends any[] ? [...Flatten<R>, ...Flatten<Y>] : [R, ...Flatten<Y>]) : []
type Y3 = Flatten<[[1], 2, [3, 4, [5], [6, [7, 8]]]]> // [1, 2, 3, 4, 5, 6, 7 ,8]

相关文章

  • TS 类型体操合集

    基本姿势 keyof keyof 返回一个类型的所有 key 的联合类型: 类型索引 类型索引可以通过 key 来...

  • typeScript语法

    ts类型 ts联合类型使用或 ts定义任意类型any ts定义函数返回值的类型 ts中的类定义 interface接口

  • Ts 进阶使用指南

    # Ts 使用指南 ## 6、参数类型和返回类型 ```ts // 参数类型和返回类型 function crea...

  • TS高级类型:Extract 与 Exclude

    Extract 是TS提供的一个TS高级type类型【简称TS高级类型】 Extract 用于类 Extract ...

  • 类型体操

    不得不说,技术栈从js切换到ts的精力开销还是挺大的. 将之前已经做好的一个页面用TS重构, 居然花费了整整一天的...

  • typescript笔记--类型篇

    动态监听ts文件变动,自动编译js 生成ts配置文件 类型约束的基本语法 ts的类型: 1.number类型:数字...

  • TS入门2019-08-13

    ts和js出了类型声明其他的都是一样的。(ts就是比js多了一个类型声明) ts 与 js 相比,其实就是把类型给...

  • 2021-01-28

    一:ts初步了解 基础类型 ts的基础类型中有如下几种:boolean/number/string/object/...

  • ts 类型

    any表示任意类型void则可以看作与any相反,意思是没有类型,通常一个函数如果没有返回值,那么就可以把他的返回...

  • ts类型

    1、基础类型 常用:string, number, boolean, array, enum, any, void...

网友评论

      本文标题:TS 类型体操合集

      本文链接:https://www.haomeiwen.com/subject/bvkbjrtx.html