Skip to content

Instantly share code, notes, and snippets.

@eczn
Last active May 9, 2022 08:33
Show Gist options
  • Select an option

  • Save eczn/df80907d9b8235a54cd6e273d6df4e51 to your computer and use it in GitHub Desktop.

Select an option

Save eczn/df80907d9b8235a54cd6e273d6df4e51 to your computer and use it in GitHub Desktop.
莲学派 TypeScript 重新理解 never

条件类型中的分配律

TypeScript Conditional Types 条件类型在设计上是 distributable 的, 也就是可以按分配律,也可以不按分配律做处理,具体来说,只有在有泛型参数出现的情况下才会按 distributive 做处理,理由是底层实现如此:

image

而什么是分配律?其实跟乘法的分配律一样,是代数系统一种概念, 如:

type ABABC =
   // 下面这个计算, 其实 TS 内部将会根据分配律进行处理
  'A' | 'B' extends 'A' | 'B' | 'C' ? 1 : 2; // got 1

// 其中过程需要将左边的 'A' | 'B' 拆开跟右边的 A B C 单独做 extends 最后将结果用 | 链接起来作为 union, 即分配律:
type ABABC_2 = (
    ('A' extends 'A' | 'B' | 'C' ? 1 : 2) |
    ('B' extends 'A' | 'B' | 'C' ? 1 : 2)
); // got 1

IsNever<T> 如何实现 ... never 到底是什么 ?

有人说, never 是空集, 可是这个 到底是什么 ? 要理解 never 我们仅需在 TS 中自己实现一个判断 IsNever 就能理解了,而我写的 IsNever 表现上很奇怪:

type A1 = never extends never ? 1 : 2; // got 1  never 是 never 这没有问题
type B1 = 11111 extends never ? 1 : 2; // got 2  11111 不是 never 所以是 2 也没有问题

// 观察到上面的 A1 B1 可以发现用 T extends never 实现, 从而将这种模式提取为泛型:
type IsNever<T> = T extends never ? 1 : 2;

// 然后我们试试
type A2 = IsNever<never>; // 然而 A2 居然是 never, 并不是预期中的 1, 甚至不是 conditional types 的左项和右项的其中一个

为什么呢?因为 TS 底层实现的 never 其实是成员数量为 0 的 union 类型,于是根据前文的分配律,IsNever<T> 里面 T extends never 需要遵守分配律, 此时如果 T 是 never, 那就啥东西都配不出来了, 最后就得到了 never —— 一个长度为 0 的 union 类型

那到底如何实现 IsNever<T> ?

核心点在于我们需要避免 IsNever<T> 之 T 是一个 union 类型,防止它变成 distributive 的条件类型,避免前面的情况。参考了其他人的写法, 可以得到:

type IsNever<T> = [T] extends [never] ? 1 : 2;
type CCCC = IsNever<never>; // got 1
type DDDD = IsNever<11111>; // got 2

相关链接

@zhangxiang958
Copy link

Is worthy of my good teacher

@eczn
Copy link
Author

eczn commented Jun 10, 2021

Is worthy of my good teacher

thanks!

@KuoChikuan
Copy link

Gain knowledge

@Crazy492
Copy link

type ABABC = 'A' | 'B' extends 'A' ? 1 : 2;// got 2
type ABABC1 = (('A' extends 'A' ? 1 : 2) | ('B' extends 'A'? 1 : 2));// got 1 | 2

type ABABC2 = T extends 'A' ? 1 : 2;
type XX = ABABC2<'A' | 'B'>; // got 1 | 2

// my good teacher eczn bozhi sensei could you plz teach me why?

@eczn
Copy link
Author

eczn commented Jul 9, 2021

type ABABC = 'A' | 'B' extends 'A' ? 1 : 2;// got 2
type ABABC1 = (('A' extends 'A' ? 1 : 2) | ('B' extends 'A'? 1 : 2));// got 1 | 2

type ABABC2 = T extends 'A' ? 1 : 2;
type XX = ABABC2<'A' | 'B'>; // got 1 | 2

// my good teacher eczn bozhi sensei could you plz teach me why?

只有在 T exntends 的时候才会应用分配律,已经补充了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment