前言
上一篇, 我们提到, TypeScript 进阶有 3 个阶段.
第一阶段是 "把 TypeScript 当强类型语言使用", 我们已经介绍完了.
第二阶段是 "把 TypeScript 当编程语言使用"
这篇主要就是介绍这个.
逻辑类型
C# 是没有办法表达出类型间的逻辑关系的. 你不能表达 "这个变量的类型是那个函数的第一个参数类型".
但 TS 可以!
function doSomething(str: string): void {} // const str : string = ''; // C# 只能 hardcode 声明类型是 string const str: Parameters<typeof doSomething>[0] = ''; // TS 可以表达出 "这个变量的类型是那个函数的第一个参数类型"
Parameters<typeof doSomething>[0] 的意思是, 这个类型是 doSomething 函数的第一个参数类型.
我们先不去理解这个语法是怎么回事. 只要知道 TS 可以表达出这个意思就好了.
类型 transform
类型 A > transformation> 类型 B
transform 指的是从一个类型, 经过一个 transform process, 变成另一个类型.
这有点像 RxJS stream 中间一堆 pipe operator 那样.
看例子
// 有个 Person Object Literal Type type Person = { str: string; num: number; }; // 我想搞一个 PromisePerson, 它拥有所有 Person 的属性, 只是类型变成 Promise<原本类型> // hardcode 的方式 type PromisePerson = { str: Promise<string>; num: Promise<number>; }; // transform 的方式 type PromisePerson1 = { [key in keyof Person]: Promise<Person[key]> };
我们先不关心它的语法, 只要知道 TS 有这个表达力就好.
由浅入深 (TS 基础编程语法)
我们先学几招简单的 TS 编程语法 > 然后再学几招 TS build-in 封装好的 Utility > 最后再把所有语法学全 > 下一篇我们才学如何把语法融会贯通写出自己的 Utility.
define variable
编程语言最基本的功能之一就是定义变量, 定义变量有两个主要目的, 一是为了 code study, 二是为了复用.
上一篇我们介绍过 Type Aliases, 它其实就是 TS 语言中的 define variable (声明变量)
type WhatEverName = string;
type NumberOrStringArray = number[] | string[];
TS 整个语言的目的就是去声明/管理 JS 的类型. 所以 TS variable 的 value alwasy is Type, 这个概念我们要记清楚.
上面例子中, 我声明了 2 个 variables, 分别存放了不同的类型, (注: TS 的 variable case style 是 CamelCase 哦).
typeof
typeof 是 JS 的语法, 但同时它也是 TS 的语法.
首先我们要先学会区分 TS 语句 和 JS 语句.
const value = typeof ''; // 这句是 JS type MyType = typeof value; // 这句是 TS
很简单, 哪一句是 JS 你一定会知道, 其余的就是 TS 咯 (TS 语句基本上都是 starts with 'type' 或者 'declare ' 这类的)
那 typeof 有啥用呢?
它可以从 JS 变量中提取出 TS 类型.
上面例子中 value 的类型是 string, 在 TS 语句中通过 "typeof value" 引入 JS 语句的 value, 它就能把 value 的类型放入到 MyType 变量中.
为什么要这样搞呢? 让 TS 语句和 JS 语句这样传来传去不是挺乱的吗?
之所以需要 typeof 是因为类型推断. 为了少写代码, 许多时候我们是不声明类型的. 而这时碰巧我们需要利用这些类型, 那么就只能从 JS 语句中提取了.
在看几个例子感受以下
function doSomething(str: string) { return 0; } class Person {} const values = [1, 2, 3]; type MyFunction = typeof doSomething; // (str: string) => number type MyArray = typeof values; // number[] type MyClass = Person; // 注意: class 不需要 typeof
注意 class 是不需要 typeof 的哦.
collection / array / set
集合也是一门语言重要的概念, 用来表达集合的 TS 语法是 Union Types.
type MyArrayType = string | number | boolean; // 用 JS 来描述大概是这样 const myArrayType = ['string', 'number', 'boolean'];
有了集合就少不了迭代语法. 但这里我们先不展开迭代语法. 下一 part 才详细讲.
function & call
编程语言除了有变量, 集合概念, 还有一个重要的概念就是函数. TS 的变量用来封装类型, 函数则用来封装 transform 的过程.
它长这样
type MyFunction<T> = T | string; type MyType = MyFunction<number>; // MyType is number | string
有点像是 Type Aliases + 泛型的味道. 但我不建议你这样去理解它. 把它当函数看就好了.
MyFunction 是函数的名字.
<T> 是参数
= T | string 是 return
MyFunction<number> 就是 call 这个函数并传入参数 number (记住, TS 都是在处理类型, 所以参数值都是类型)
type MyFunction<T> = T | string; type MyType = MyFunction<number>; // MyType is number | string // 用 JS 来描述大概是这样 const myFunction = (value) => [value, 'other value']; const myType = myFunction('value'); // myType = ['value', 'other value']
小结
到这里, 我们介绍了 TS 作为编程语言的 3 大特性, variable 变量, set 集合, function 函数.
利用这些编程手法, 我们就可以表达出逻辑类型和 transform 类型了. 下一 part 我们学一些 TS build-in 的 Utility
TypeScript Build-in Utility
Utility 是啥? 用过 JS Lodash Library 的就知道, 它就是许许多多小方法的库, 其目的就是让代码复用和干净. 、
TS 的 Utility 也是同样的, 它有许许多多小方法 (TS function), 让我们 transform 类型.
这 part, 我们不去探索这些 utility function 底层语法是怎样的, 我们只学它的功效, 调用就好.
Partial<Type>
Partial 函数的功能是 transform Object Literal / Class / Interface (之后统称 Object) 的所有属性变成 optional property.
type Person = { str: string; num: number; }; type PartialPerson = Partial<Person>; // 等价于 type PartialPerson1 = { str?: string; // optional property num?: number; // optional property };
题外话, 学习这类 Utility 库 (e.g. RxJS operators), 具体使用场景不重要, 我们把所有方法过一遍, 有个印象就好. 等项目遇到问题的时候, 回来翻一翻就可以了.
Required<Type>
和 Partial 相反, 它是把 optional property 变成 not optional
type PartialPerson = { str?: string; num?: number; }; type Person = Required<PartialPerson>; // 等价于 type Person1 = { str: string; num: number; };
Readonly<Type>
顾名思义, 就是把 Object / Array 变成 Readonly
type Obj = { str: string; num: number }; type Arr = string[]; type ReadonlyObj = Readonly<Obj>; type ReadonlyArr = Readonly<Arr>; // 等价于 type ReadonlyObj1 = { readonly str: string; readonly num: number; }; type ReadonlyArr1 = readonly string[];
Record<Keys, Type>
Record 用来创建 Object Literal, 特色是所有属性拥有相同的类型.
type Obj = Record<'key1' | 'key2' | 'key3', string>; // 相等于 type Obj1 = { key1: string; key2: string; key3: string; };
第一个参数是放入所有的属性 keys, 它用一个集合来表示, 也就是 Union Types + String Literal
第二个参数是属性的 value, 也就是类型 (记住, TS 都是在处理类型, 我重复很多次了)
Record 函数最终会返回 Object Literal 类型, 它拥有所有的 keys 属性, 每一个属性的类型都相同, 就是第二参数传入的类型.
Pick<Type, Keys>
Pick 是从一个 Object 里选出指定的 keys 保留, 去掉其余的属性.
type Obj = { str: string; num: number; bool: boolean; }; type PickedObj = Pick<Obj, 'str' | 'bool'>; // 只保留 str 和 bool // 相等于 type PickedObj1 = { str: string; bool: boolean; // 没有 num 了 };
Omit<Type, Keys>
Omit 和 Pick 相反, 它是选择要删除的, 没有选中的则保留下来.
type Obj = { str: string; num: number; bool: boolean; }; type OmitedObj = Omit<Obj, 'str' | 'bool'>; // 删除 str bool 只留下 num // 相等于 type OmitedObj1 = { num: number; };
Exclude<UnionType, ExcludedMembers>
参数 1, 2 都是集合. 什么类型的集合都可以.
参数 1 是所有类型, 参数 2 是声明不要保留的类型 (和上面 Omit 的概念差不多, 其实 Omit 底层就是用了 Exclude 函数来完成的哦)
type Keys = 'key1' | 'key2' | 'key3' | 'key4'; type ExcludedKeys = Exclude<Keys, 'key1' | 'key3'>; // left 'key2' | 'key4' type Arr = boolean | string | number | null; type ExcludedArr = Exclude<Arr, boolean | number>; // left string | null // 相等于 type ExcludedKeys1 = 'key2' | 'key4'; type ExcludedArr1 = string | null;
来一个复杂点的
type Types = 'key1' | 'key2' | 'key3' | number | boolean; type ExludedTypes = Exclude<Types, string>; // left number | boolean
string 把 'key1' | 'key2' | 'key3' 都给 exclude 掉了, 其实不难理解, 因为 'key1' 是 String Literal, 它是"一种" string, 所以当声明要把 string exclude 掉时, 它自然也需要被 exclude 掉.
Extract<Type, Union>
它时 Exclude 的相反. 参数 2 声明的类型都是要保留的类型.
type Types = 'key1' | 'key2' | number | boolean; type ExtractedTypes = Extract<Types, string | boolean>; // left boolean | "key1" | "key2"
NonNullable<Type>
参数是一个类型集合, 它会把集合内的 null 和 undefined 类型过滤掉, 留下其它的
type Types = string | undefined | number | null | boolean; type NonNullableTypes = NonNullable<Types>; // left: string | number | boolean