CD's blog CD's blog
首页
  • HTMLCSS
  • JavaScript
  • Vue
  • TypeScript
  • React
  • Node
  • Webpack
  • Git
  • Nestjs
  • 小程序
  • 浏览器网络
  • 学习笔记

    • 《TypeScript 从零实现 axios》
    • Webpack笔记
  • JS/TS教程

    • 《现代JavaScript》教程
🔧工具方法
  • 网站
  • 资源
  • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

CD_wOw

内卷的行情,到不了的梦
首页
  • HTMLCSS
  • JavaScript
  • Vue
  • TypeScript
  • React
  • Node
  • Webpack
  • Git
  • Nestjs
  • 小程序
  • 浏览器网络
  • 学习笔记

    • 《TypeScript 从零实现 axios》
    • Webpack笔记
  • JS/TS教程

    • 《现代JavaScript》教程
🔧工具方法
  • 网站
  • 资源
  • Vue资源
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • TypeScript类型梳理
  • TypeScript联合、交叉类型解读
  • TypeScript枚举用法解读
  • keyof用法梳理
  • extends用法梳理
    • extends做条件批判联合类型
    • 两个接口类型的比较
    • extends判断字符串
  • type-challenges笔记
  • TypeScript笔记
CD_wOw
2022-09-04
目录

extends用法梳理

做了type-challenges之后,记录对extends的理解。

# extends做条件批判联合类型

先看例子

type Test<T, T2 = T> = T extends T2 ? {t: T, t2: T2} : never
type u = Test<"2" | "3", "2" | "3">;
// 结果
type u = {
    t: "2";
    t2: "2" | "3";
} | {
    t: "3";
    t2: "2" | "3";
}
1
2
3
4
5
6
7
8
9
10

如上:

当extends做条件限制联合类型 T extends T2 时,所作的比对是 "2" | "3" extends "2" | "3",它在ts中其实会被转换成 ("2" extends "2" | "3") | ("3" extends "2" | "3"), 即将联合类型中的T的内容遍历出来与T2进行比对。随后将结果以联合类型返回。

那么当我们想要去判断联合类型入参T是否与T2全等时, 我们该怎么做呢?

type Test<T, T2 = T> = [T] extends [T2] ? {t: T, t2: T2} : never
type u = Test<"2" | "3", "2" | "3">;
// 结果
type u = {
    t: "2" | "3";
    t2: "2" | "3";
}
1
2
3
4
5
6
7

我们可以发现,放置方括号以避免这次分配,即转变成比对数组内元素的类型是否相等。

我们来通过拆解道题加深理解IsUnion (opens new window):

type case1 = IsUnion<string>; // false
type case2 = IsUnion<string | number>; // true
type case3 = IsUnion<[string | number]>; // false

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


// 当 T extends T2 即我们可以得到前面的一段结果
type Test<T, T2 = T> = [T] extends [T2] ? {t: T, t2: T2} : never
type u = Test<"2" | "3">;
// 结果
type u = {
    t: "2";
    t2: "2" | "3";
} | {
    t: "3";
    t2: "2" | "3";
}
// 此时经过T extends T2 ? 加工后 的T 值已经变成了联合类型中的单个变量"2"和"3", 接下来 [T2] extends [T] 即
["2" | "3"] extends ["2"] ?
["2" | "3"] extends ["3"] ?
// 通过增加方括号消除了extends对联合类型分配的约束。很显然此时的"2" | "3"是不属于["2"] 的,返回false。那么当我们输入单个字符时,即
type u = Test<"2">;
"2" extends "2"? // true
["2"] extends ["3"] ? // true
// 显然就不是元组类型了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

# 两个接口类型的比较

X extends Y − 判断 X 是否为 Y 的子类型

type ButtonProps = {
  size: 'mini' | 'large',
  type: 'primary' | 'default',
}

type MyButtonProps = {
  size: 'mini',
  type: 'primary' | 'default',
  color: 'red' | 'blue'
}

type IsSubButton = MyButtonProps extends ButtonProps ? true : false; // true
1
2
3
4
5
6
7
8
9
10
11
12

对于两个接口类型,extends 会先将接口类型中的每一个属性拿出来比较, 相当于

type ButtonProps = 'size' | 'type'
type MyButtonProps = 'size' | 'type' | 'color'

MyButtonProps extends ButtonProps
// 等同于
'size' extends 'size' | 'type' | 'color' ? true : false
'type' extends 'size' | 'type' | 'color' ? true : false
1
2
3
4
5
6
7
  1. 当extends发现 两者之中后者的属性比对结果都为 true 后,再拿出属性的内容进行比对(可以理解为取包含关系):
// size
'mini' extends 'mini' | 'large' ? true : false
'primary' | 'default' extends 'primary' | 'default' ? true : false
// =>等同于
'primary'extends 'primary' | 'default' &'default'extends 'primary' | 'default'? true : false
1
2
3
4
5
  1. 可以看到 'mini' 属于联合类型 'mini' | 'large' 中的一份子,即可由其生成,返回true,同理 'primary' | 'default' 也是如此,由此得出 MyButtonProps extends ButtonProps 的结果为true

我们再看增加了一个属性之后的结果

type ButtonProps = {
  ...
  type2: 'primary' | 'default',
}
type IsSubButton = MyButtonProps extends ButtonProps ? true : false; // false
1
2
3
4
5

由上面的第一步可以得知, MyButtonProps的属性里并没有 type2 这个类型,所以不能由 ButtonProps 获得,返回 false

对于属性的值的判断,同时还包括类型缩减

type ButtonProps = {
  // size: 'mini' | 'large',
  // type: 'primary' | 'default',
  size: string
}
type MyButtonProps = {
  size: 'mini',
  type: 'primary' | 'default',
  color: 'red' | 'blue'
}
type IsSubButton = MyButtonProps extends ButtonProps ? true : false; // true

type ButtonProps = {
 size: 'mini' | 'large',
}
type MyButtonProps = {
  size: string,
  type: 'primary' | 'default',
  color: 'red' | 'blue'
}
type IsSubButton = MyButtonProps extends ButtonProps ? true : false; // false
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

我们将size值更换为了string类型,string类型和MyButtonProps的size(字面量string类型)来说,他们是父子的关系,所以MyButtonProps的size可以由 ButtonProps 的size继承得出,返回true.

反过来MyButtonProps值为了string类型时,则是出现了类型拓宽,很显然ButtonProps的size子类型并不能变成string父类型,返回false

# extends判断字符串

当我们需要书写对字符串进行操作的TS时,我们常常通过 extends ${xxx} 或是 extends ${infer X} 来展开实现。

  1. 当我们使用${xxx}时:
type IsSame<T extends string, K extends string> = T extends `${K}` ? true:false;
type w = IsSame<'abc', 'abc'> // true
type w = IsSame<'abc', 'abd'> // false
1
2
3

显然得知:当我们在字符串之间进行比较时, extends会同等的比较一整个字符串是否全等。

  1. 当我们采用${infer X}时:
type GetAll<T extends string> = T extends `${infer K}`
? {
  v:K
}
: T;

type q = GetAll<'abc'> //  { v: "abc"; }
type GetFirst<T extends string> = T extends `${infer K}${infer U}`
  ? {
    v:K
  }
  : T;

type w = GetFirst<'abc'> // { v: "a";}
type d = GetFirst<'bc'>// { v: "b";}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

从上我们可以看出,当我们采用${infer K}(infer 我们可以理解成将ts类型参数化)时,我们返回的是一整个string字符串, 当我们${infer K}${infer U}时,每一次返回的都是字符串的首字母,我们通常借由这点通过递归的形式遍历整个字符串,举个例子:

对于${infer K}:

// 实现 Capitalize<T> 它将字符串的第一个字母转换为大写,其余字母保持原样。
type Capitalize<T extends string> = T extends `${infer U}${infer K}`
  ? `${Uppercase<U>}${K}`
  : T;
type capitalize = Capitalize<"hello world">; // expected to be 'Hello world'
1
2
3
4
5

这里我们通过infer拿到首字母,并通过字符串方法Uppercase将首字母转换成大写抛出。

// 实现一个将接收到的String参数转换为一个字母Union的类型。
type StringToUnion<T extends string, K = never> = T extends `${infer V}${infer U}`? V extends never ? K : StringToUnion<U, K | V> : K;


type Test = '123';
type Result = StringToUnion<Test>; // expected to be "1" | "2" | "3"
1
2
3
4
5
6

这里我们通过infer拿到首字母, 再进入StringToUnion遍历字符串的所有字母从而达到了将字符串转换为联合类型的目的。 对于字符串的操作同样适用于元组

编辑 (opens new window)
#TypeScript
上次更新: 2023/05/03, 18:01:41
keyof用法梳理
type-challenges笔记

← keyof用法梳理 type-challenges笔记→

最近更新
01
gsap动画库学习笔记 - 持续~
06-05
02
远程组件加载方案笔记
05-03
03
小程序使用笔记
03-29
更多文章>
Theme by Vdoing | Copyright © 2020-2023 CD | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式