TypeScript 入门指南

# TypeScript 简介

# 什么是 TypeScript

一个 JavaScript 超集,包含类型系统,以及其他一些功能。

随着 Promise、Generators 等 API 进入 JS 标准,
TS 和 JS 在这些 API 方面的差别在变小,
但 TS 还有一些独有特性,这些特性才是和 JS 的主要区别:

  • 类型系统、type-checking
  • 类型(自动)推导、auto-completion

# 为什么要用 TypeScript

  • 提升代码健壮性
  • 面向接口编程(代码自解释,并行开发)
  • 静态检查可以提高开发效率
  • 减少开发时(人工推导带来的)认知负荷

# 学习 TypeScript

# 概览

  • 耗时:从入门到熟悉需要大约 20~40 小时(个人估计)
  • 难点:
    • 理解 JS 中没有的 TS 特性
    • 训练 TS 的高级用法(泛型、交集、条件等)
  • 工具:

# 学习路线

  • 前置学习
  • 学习 TypeScript
    • (学习 JavaScript)
    • 学习和使用 TypeScript 常用特性
      • 学习泛型和类型推导——TS 最难的部分之一
    • 尝试将以前写的 JS 代码转换成 TS 版本
    • 了解 @types (DefinitelyTyped
  • 实战
    • 写业务时逐步使用 TS (包括业务代码和 npm 包)
    • package 开发打包和声明文件
  • 进阶
    • 学习 TS 的高级用法
    • 阅读一些用库的 TS 相关源码(比如 redux
  • 迷思
    • 如何将 Object.keys 转换成 enum
    • 如何把数组转换成字面量类型
    • 如何给复杂的处理函数写类型
    • 如何写一个类型表示排除 undefined 的 any

# 资料

# 概览

# 自学教材

# 进阶/实战

# TypeScript 知识体系

# 如何运行 TypeScript

# TypeScript 主要概念

# TypeScript 典型代码

# TypeScript 基本语法

Check TypeScript - Learn X in Y minutes

// ts-node

// * -------------------------------- 基本

// * ---------------- 基本类型

let isDone: boolean = false;
let lines: number = 42;
let lastName: string = 'Anders';

type MyList = number[];

let list: MyList = [1, 2, 3];

// * ---------------- 函数的几种声明方式

let square = (n: number): number => n * n;
let square2: (n: number) => number = (n) => n * n;

type Sq = (n: number) => number;
let square3: Sq = (n) => n * n;

interface Sq4 {
  (n: number): number;
}
let square4: Sq4 = (n) => n * n;

// * ---------------- 枚举

enum Color {
  Red = 'Red',
  Green = 'Green',
  Blue = 'Blue',
}

// * ---------------- 接口、类

interface CarInterface {
  model: string;
  radio?;
  getYear(): any;
}

class Car implements CarInterface {
  constructor(
    readonly model,
    private engine,
    public radio?,
  ) {} // 简写法

  static Manufacturer: string = 'BMW';
  getYear() {}
}

// * ---------------- 泛型

class Observable<T> {
  constructor(public value: T) {}
}

let myOb: Observable<number> = new Observable(2);

// * -------------------------------- 示例

// * ---------------- 接口、对象

interface MyDict {
  type: string;
  [name: string]: string; // 可索引
}

const dict: MyDict = {
  type: 'a dict',
  num: 233, // Error!
  word: 'hello', // Valid
};

// * ---------------- 递归的声明

type NumVal = 1 | 2 | 3 | NumVal[];
const nestArr: NumVal = [1, 2, 3, [1, 2, [3]]];

# TypeScript 实用代码

# 联合转交集

type UnionToIntersection<U> = (U extends any
? (k: U) => void
: never) extends (k: infer I) => void
  ? I
  : never;

# 复杂的类型声明

infer - 深入理解 TypeScript

通过编写泛型和类型推断,能够拥有更准确的类型,
能保证业务代码拥有更充分的类型提示,提升代码整体质量。

type MapEveryToPromise<T extends object> = {
  [K in keyof T]: T[K] extends infer P ? Promise<P> : never;
};

const dataPool = {
  key1: 1,
  key2: 'hello',
  // ...
};

const advanceDataPool: MapEveryToPromise<typeof dataPool> = {
  key1: Promise.resolve(1),
  key2: Promise.resolve('hello'),
  // ...
};

# TypeScript 相关

# 泛型中的命名习惯

很多代码中使用泛型,一般只使用一个大写字母,
这些实际相当于助记符,是缩写。

  • T (for Type)
  • E (for Element)
  • K (for Key)
  • V (for Value)
  • U 没找到出处…
    推测是 T 的后一位字母,就像 i, j, k 一样…
    也可能是指 Union

其他根据具体上下文可以同样推测:

  • S State
  • A Action

# 从 JS 迁移到 TS

Migrating from JavaScript

  • 注意!
    • 不要一边迁移一边修改代码逻辑
    • 不要在低测试覆盖率时迁移
    • 初期无需写太严谨的类型(为了开发效率)
    • 别忘记写 Type 的测试
    • 不要急着上线
  • 步骤
    • 第一阶段
      • 确保测试全部通过
      • .js 文件重命名成 .ts
      • 修复类型检查错误或 TS 编译器报错
      • 别改代码逻辑
      • 确保测试全部通过
    • 第二阶段
      • 确保测试全部通过
      • 禁止隐式 any{"noImplicitAny": true }
      • 尽量明确类型
      • 确保测试全部通过
    • 第三阶段
      • 逐步升级,小步 commit
      • 打开全部的 strict 选项 (Compiler Options
      • 将显式 any 替换成具体类型
      • 尽力避免不安全类型声明

# TypeScript 和 Flow

Flow 是另一个 JS + type 超集,来自 Facebook。
TypeScript 来自微软,在特性上更丰富。

现在 TypeScript 领先 Flow,
几乎成为了新的 JS 开发标准了(尤其是大型工程)。

# TypeScript 是如何工作/运行的

不严谨但简单的理解:

  • 发布和执行前编译,完全退化成合法 JS
    • 类型系统全部丢弃
    • 无法丢弃的特性会被转义(比如 enum => Object)

可以尝试 Babel 在线编译 来理解

TypeScript 编译原理 - 深入理解 TypeScript

# TypeScript 是如何基于 JavaScript 被开发出来的

这个问题需要扎实的 CS 基础作为铺垫…

如何看懂 typescript 核心源码,并可以参照 typescript 写一个类似的项目?

# TypeScript 有哪些不足

目前不支持高阶泛型

Allow classes to be parametric in other parametric classes

导致让这个 demo typescript-generic-problem
中的全部代码都拥有准确的类型是一件很困难的事情,
只能修改业务代码,或手动进行类型声明来妥协。