TypeScript基础

探索 TypeScript 类型注解 - 类型编程

2020-01-11  本文已影响0人  zhilidali

Exploring TypeScript Type Annotations - Type Programming

本文由 WowBar 团队首发于 GitHub

作者: zhilidali

欢迎来到 《探索 TypeScript 类型注解》 系列教程。

上一篇介绍了 TS 的高级类型。
本篇将前面的知识点融会贯通,将对类型的探索提升一个层次:从类型层面进行编程。

目录

类型编程

首先,我们回顾一下前几节对类型的探索。

根据 TS 提供的 数据类型 以及声明的自定义类型,结合高级类型中的 操作符 可以对类型进行各种运算 (高级类型本质上就是各种操作符表达式)。

再加上具有函数功能的 泛型,可以对类型的运算进行封装、复用、组合。要知道函数是 JS 中最强大的武器,谁说“类”来着,算了,好累,我还要(搬砖)拯救世界。还要知道,TS 没有采用传统面向对象语言使用的名义类型,而是基于偏向于函数式编程的结构类型,(JS 是多范式编程语言)。

到此为止,我们已经具备了对类型进行编程的各种工具 (程序 = 数据结构 + 算法),接下来各位童鞋就可以发挥无穷的智慧了。

童鞋请留步!俗话说吃人嘴短,拿人手软,请先让巨硬(微软大大)炫个富。下面介绍 TypeScript 官方标准库中封装的实用工具类型。

实用工具类型

TypeScript 提供的实用工具类型用来实现常见的类型转换,这些类型工具函数是全局可见的。

Extract,Exclude,NonNullable

使用示例

type foo = Extract<number | string, string>; // string
type bar = Exclude<number | string, string>; // number
type baz = NonNullable<number | string | null | undefined>; // string | number

具体实现

// 主要使用条件类型 `T extends U ? X : Y` 实现
type Extract<T, U> = T extends U ? T : never;
type Exclude<T, U> = T extends U ? never : T;
type NonNullable<T> = T extends null | undefined ? never : T;

Partial, Require, Readonly

使用示例

interface Type { a: number, b?: string };
let foo: Partial<Type> = { b: 'b' };
let bar: Required<Type> = { a: 1 }; // Error
let baz: Readonly<Type> = { a: 1 };
baz.a = 2; // Error

具体实现

// 主要使用映射类型 `[K in T]: Type` 及索引类型 `keyof T`、`T[P]` 实现
type Partial<T> = { [P in keyof T]?: T[P] };
type Require<T> = { [P in keyof T]-?: T[P] }; // 注意这里的 `-?`
type Readonly<T> = { readonly [P in keyof T]: T[p] };

TypeScript 标准库中提供了许多实用的工具类型,而且随着 TypeScript 不断更新迭代,会有更多的实用工具类型加入到标准库中,此处不在重复介绍(提示:实用工具很实用),详情请移步官方手册,手册中给出了详细的使用示例。对于这些工具类型的具体实现,请移步官方仓库的 lib

typeof

在 TS 中,还可以使用 typeof 来获取变量的类型。

let foo: number = 3;
type bar = typeof foo; // 相当于 type bar = number

extends

前面的章节中多处使用了 extends 关键字。如下

原生 JS 中类的继承

class A { a: number }
class B extends A { b: string } // B 继承 A
let a: A = new A();
let b: B = new B();

a = b; // Ok, A = B 少兼容多,子类兼容超类

接口继承

interface A { a: number }
interface B extends A { b: string }
let a: A = { a: 1 };
let b: B = { b: 'b', a: 1 };

a = b; // Ok, A = B

泛型约束

interface A { a: number }

let foo: <B extends A>(arg: B) => void;
foo({ a: 1, b: 2});

条件类型

interface A { a: number }
interface B { a: number, b: string }
type E = B extends A ? true : false;
// type E = true

汇总如下

以上均有共同的形式 Sub extends Sup

  1. extends 关键字的语义:它们之间属于继承关系,即子类(型)继承超类(型)。
  2. 从类型兼容性角度:超类型兼容子类型,即子类型可以赋给超类型。
  3. 从功能上:
    • 类和接口中的 extends 用来定义,可有多个超类 Sup,中间用 , 分割。
    • 泛型约束和条件类型中的 extends 用来检测兼容性,即 Sup 是否兼容 Sub

类型与集合

既然是编程,下面从数学的角度来简单粗略地描述 TS 的类型系统,(读者可略过,想深入的童鞋可移步:https://zhuanlan.zhihu.com/p/38081852)。

TS 中的类型好比数学中的集合,类型是具有某种特定性质的 JS 值的集合。
比如 number 类型对应 JS 中所有数值的集合。

类型集合的分类

结语

本篇主要是对类型进行编程的能力进行了梳理。

协议

本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。

《探索 TypeScript 类型注解》

上一篇下一篇

猜你喜欢

热点阅读