上面这个是今天会提到的内容,如果你已经可以轻松看懂,欢迎直接左转去看我队友们的精彩文章!
昨天我们对於 Utility Types 有了更多认识,发现原来 Utility Types 可以像函式一样接受型别当作 input 然後回传一个新的型别当作 output。今天来看一下原来在 TypeScript 中,型别还可以做到「条件判断(Conditional Types)」!?
在 TypeScript 中,虽然没办法针对型别使用 if ... else
来做条件判断,但可以使用 JavaScript 中同样有的三元运算子(ternary operator)来达到条件判断的目的,也就是用 ?
和 :
。
语法大概会像这样:
这里读者可以留意到 X extends Y
就像是一个放在 if()
括号内的条件式,这里的 extends
就像先前在泛型时提到过的,要当作是「能够满足」的意思。X 能够满足 Y,更确切来说是指「X 是 Y 的子集合(subset)」。
在学习 Conditional Types 或 Utility Types 时,可以多利用 Type Alias 的方式,建立一个型别後,看看它最终会是什麽。以 Conditional Type 来说,可以用它来建立一个名为 NewType
型别:
type NewType = X extends Y ? true : false;
上面的意思翻成白话文就是「如果 X 是 Y 的子集合的话,则 NewType 就会是 true,否则的话 NewType 会是 false」。
关於 extends
的使用我喜欢用「集合」搭配图像的方式来思考,X extends Y
表示 Y
是比 X
更大的集合:
如果你觉得还有点太过抽象,可以把抽象的东西用一些具体的值带入,可以帮助我们更好了解。
现在再把它更具体一点:
SomeType1
,让它的值直接是个字串(String Literal Types)NewType1
type SomeType1 = 'any-string'; // String Literal Types
type NewType1 = SomeType1 extends string ? true : false; // true
这时因为 SomeType1
是 String Literal Types,它是 string
的子集合,所以 NewType1
就会是 true
。用图像来描述的话会像是这样:
接着定义另一个 SomeType2
,让它的值直接是个数字(Number Literal Types):
type SomeType2 = 0; // Number Literal Types
type NewType2 = SomeType2 extends string ? true : false; // false
这时候因为 SomeType2 是 Number Literal Types,并不是 string
的子集合,因此 NewType2
就会是 :
後的 false
。用图像来表示的话会是这样:
这就是 Conditional Types 的语法。Conditional Type 虽然看起来简单,但实际上可以做出很多不同的变化,我们会在後面几天一直看到它的身影。
在 TypeScript 中,可以直接使用「布林值」、「字串值」和「数值的值」作为型别,这种用法称作 Literal Types。
在了解 Conditional Types 的使用後,让我们来看官网提供的 Flatten 这个范例, Flatten
一样可以视为一个 Utility Type:
让我们先来理解一下 T extends any[]
是什麽意思。
除了把抽象的东西用一些具体的值带入之外,在理解 Conditional Types 时,可以把 ?
和 :
後的内容先替换成其他内容,方便理解它是跑到了那一个条件。
这里先把 ?
後的值改成 true
,:
後的值改成 false
,像是这样:
// 当我们不清楚「条件」是什麽意思是,可以修改 ? 和 : 後的内容
type Flatten<T> = T extends any[] ? true : false;
接着分别定义型别 Manufacture
和 Manufactures
,然後把它带入 Flatten
的 <>
中:
type Manufacture = 'Apple' | 'Google' | 'Samsung' | 'Sony';
type Manufactures = Manufacture[];
type FlattenManufacture = Flatten<Manufacture>; // false
type FlattenManufactures = Flatten<Manufactures>; // true
把滑鼠移到 FlattenManufacture
或 FlattenManufactures
就可以看到回传的型别:
你会发现如果带入的是 Manufacture
,因为 Manufacture
不是 any[]
的子集合,因此会进到 false
:
相反地,如果带入的是 Manufactures
,以为 Manufactures
本身就是 Manufacture[]
,可以算是 any[]
的子集合,进而得到 true
:
搭配原本对於 TypeScript 的理解,应该就可以知道 T extends any[]
翻成白话文,指的就是「T 是否符合阵列型别」。
在理解 Condition 後,接着让我们把 Flatten
改回原本的样子:
type Flatten<T> = T extends any[] ? T[number] : T;
现在我们可以理解,当 T
不是阵列型别的子集合时,它会直接回传 :
後的 T
,也就是什麽都不做直接回传原本的型别。但如果 T
是阵列型别的子集合,那 T[number]
是什麽意思呢?
同样的,我们可以带一个实际的型别来取代 T
帮助我们理解。我们把 T
用符合阵列型别的 Manufactures
带进去看看:
type Manufacture = 'Apple' | 'Google' | 'Samsung' | 'Sony';
type Manufactures = Manufacture[];
type ShowMeTheType = Manufactures[number];
接着把滑鼠移到 ShowMeTheType
上面:
你会发现,因为 Manufactures
的型别是 Manufacture[]
,而使用 Manufactures[number]
的意思其实就是把阵列型别里的元素型别取出的意思(可以参考内文後方针对 Index Access Types 的补充)。
回到一开始的范例,可以知道当 T
是阵列型别的子集合时,会得到的是 T[number]
,也就是取出 T
这个阵列型别中元素的型别。所以说 Flatten
这个 Utility Type 的作用就是「把阵列型别中的元素型别摊平後回传,如果不是阵列元素就什麽都不要做」的意思。
使用上可以像这样:
type FlattenManufacture = Flatten<Manufacture>; // "Apple" | "Google" | "Samsung" | "Sony"
type FlattenManufactures = Flatten<Manufacture[]>; // "Apple" | "Google" | "Samsung" | "Sony"
type Example1 = Flatten<['a', true, 3]>; // true | 3 | "a"
type Example2 = Flatten<string[]>; // string
type Example3 = Flatten<'not array'>; // "not array"
稍微补充一下,在 Day 05 的时候我们有提了 Indexed Access Types,当时提到的是针对物件型别来使用 Indexed Access Type 的话,可以取出物件型别中属性值的型别,但如是针对阵列型别的话,这可以使用 Indexed Access Types 来取出阵列型别中元素型别,因为阵列型别的 index 一定是 number
型别的缘故,所以只要使用 SomeArray[number]
就可以取出 SomeArray
这个阵列型别里面元素值的型别。
举例来说:
type StringArray = string[];
type StringArrayElement = StringArray[number]; // string
type NumberArray = number[];
type NumberArrayElement = NumberArray[number]; // number
当然也可以用实际数值的方式取出某 index 元素的型别:
type SomeArray = [string, boolean, number];
type Element0 = SomeArray[0]; // string
type Element1 = SomeArray[1]; // boolean
type Element2 = SomeArray[2]; // number
type Elements = SomeArray[number]; // string | boolean | number
?
和 :
後的内容先替换成其他内容,方便理解它是跑到了那一个条件。这招非常实用,特别是在未来你可以能会看到多重的 ? A : B ? C : D
时。https://tsplay.dev/wXkB8W @ TypeScript Playground
<<: Python - Scrabble Word Finder - Python 爬虫练习笔记
>>: 【Day 23】JavaScript 条件(三元)运算子
Build a CPU tags: IT铁人 抽象化设计 建构一台电脑时,他要能执行所有指定ISA的...
前言 Service Catalog 介绍参考文件 https://kubernetes.io/do...
Although YouTube Music hit the global market a few...
Hello World 的起源 通常我们在测试一个程序或是一个环境是否正常 会习惯使用 Hello ...
在 OpenTelemetry 中有提到 trace 的概念。而 Trace 由多个 Span 组成...