1. What is TypeScript?

TypeScript 是 JavaScript 的一个超集,主要提供了类型系统对 ES6 的支持,它由 Microsoft 开发,代码开源于 GitHub 上。
其次引用官网的定义:

TypeScript is a typed superset of JavaScript that compiles to plain JavaScript. Any browser. Any host. Any OS. Open source.

翻译成中文即是:

TypeScript 是 JavaScript 的类型的超集,它可以编译成纯 JavaScript。编译出来的 JavaScript 可以运行在任何浏览器上。TypeScript 编译工具可以运行在任何服务器和任何系统上。TypeScript 是开源的。

2. Why TypeScript?

TypeScript 官网列举了一些优势,大致也可分为以下几点:

2.1 TypeScript 增加了代码的可读性和可维护性

  • 类型系统实际上是最好的文档,大部分的函数看看类型的定义就可以知道如何使用了
  • 可以在编译阶段就发现大部分错误,这总比在运行时候出错好
  • 增强了编辑器和 IDE 的功能,包括代码补全、接口提示、跳转到定义、代码重构等

    2.2 TypeScript 非常包容

  • TypeScript 是 JavaScript 的超集,.js 文件可以直接重命名为 .ts 即可

  • 即使不显式的定义类型,也能够自动做出类型推论
  • TypeScript 的类型系统是图灵完备的,可以定义从简单到复杂的几乎一切类型
  • 即使 TypeScript 编译报错,也可以生成 JavaScript 文件
  • 兼容第三方库,即使第三方库不是用 TypeScript 写的,也可以编写单独的类型文件供 TypeScript 读取

    2.3 TypeScript 拥有活跃的社区

  • 大部分第三方库都有提供给 TypeScript 的类型定义文件

  • Angular、Vue、VS Code、Ant Design 等等耳熟能详的项目都是使用 TypeScript 编写的
  • TypeScript 拥抱了 ES6 规范,支持 ESNext 草案中处于第三阶状态(Stage 3)的特性

    2.4 TypeScript 的缺点

    任何事物都是有两面性的,TypeScript也不例外 :

  • 有一定的学习成本,需要理解接口(Interfaces)、泛型(Generics)、类(Classes)、枚举类型(Enums)等前端工程师可能不是很熟悉的概念

  • 短期可能会增加一些开发成本,毕竟要多写一些类型的定义,不过对于一个需要长期维护的项目,TypeScript 能够减少其维护成本
  • 集成到构建流程需要一些工作量
  • 可能和一些库结合的不是很完美(需要库提供.d.ts声明文件或者需要自行定义)

    2.5 与JavaScript的对比

    | 特性 | TypeScript | JavaScript | | —- | —- | —- | | 明确类型要求 | √ | × | | 类的概念 | √ | × | | 静态类型检查 | √ | × | | 函数缺省参数值 | √ | × | | 模块的概念 | √ | × |

3. How to use TypeScript?

安装 TypeScript

TypeScript 的命令行工具安装方法如下:

  1. npm install -g typescript

以上命令会在全局环境下安装 tsc 命令,安装完成之后,我们就可以在任何地方执行 tsc 命令了。
编译一个 TypeScript 文件很简单:

  1. tsc hello.ts

在webpack集成项目中使用TypeScript

  1. // 生成tsconfig.json文件
  2. tsc --init
  3. // 安装ts-loader依赖
  4. npm i ts-loader -D
  5. // 添加ts-loader
  6. rules: [{
  7. // ts文件
  8. test: /\.ts$/,
  9. loader: 'ts-loader',
  10. options: { appendTsSuffixTo: [/\.vue$/] } // 若你的项目是以vue来开发,也需要对.vue文件进行识别
  11. }]

4. TypeScript基础概念

  • 原始数据类型
    原始数据类型包括:布尔值、数值、字符串、null、undefined 以及 ES6 中的新类型 SymbolBigInt

    1. // 布尔值是最基础的数据类型,在 TypeScript 中,使用 boolean 定义布尔值类型
    2. let isDone: boolean = false;
    3. // 实质上new Boolean() 返回的是一个 Boolean 对象:
    4. let createdByNewBoolean: boolean = new Boolean(1);
    5. // Type 'Boolean' is not assignable to type 'boolean'.
    6. // 'boolean' is a primitive, but 'Boolean' is a wrapper object. Prefer using 'boolean' when possible.
    7. // 直接调用 Boolean 也可以返回一个 boolean 类型
    8. let createdByBoolean: boolean = Boolean(1);
    9. TypeScript 中,boolean JavaScript 中的基本类型,而 Boolean JavaScript 中的构造函数。其他基本类型(除了 null undefined)一样
    10. // 数值类型
    11. let decLiteral: number = 6;
    12. let hexLiteral: number = 0xf00d;
    13. // ES6 中的二进制表示法
    14. let binaryLiteral: number = 0b1010;
    15. // ES6 中的八进制表示法
    16. let octalLiteral: number = 0o744;
    17. let notANumber: number = NaN;
    18. let infinityNumber: number = Infinity;
    19. // 字符串类型
    20. let myName: string = 'Tom';
    21. // 空值类型
    22. // JavaScript 没有空值(Void)的概念,在 TypeScript 中,可以用 void 表示没有任何返回值的函数:
    23. function alertName(): void {
    24. alert('My name is Tom');
    25. }
    26. 声明一个 void 类型的变量没有什么用,因为你只能将它赋值为 undefined null
    27. let unusable: void = undefined;
    28. // Null 和 Undefined
    29. TypeScript 中,可以使用 null undefined 来定义这两个原始数据类型:
    30. let u: undefined = undefined;
    31. let n: null = null;
    32. // 值得注意的是!!两者和void的区别在于undefined 和 null 是所有类型的子类型。也就是说 undefined 类型的变量,可以赋值给 number 类型的变量。而void不行
  • 任意值

    1. // 任意值(Any)用来表示允许赋值为任意类型
    2. //变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型:
    3. let something; ==> let something: any;
  • 类型推论

    1. // 如果没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型
    2. let myFavoriteNumber = 'seven'; ==> let myFavoriteNumber: string = 'seven';
    3. // 如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查
  • 联合类型

    1. // 联合类型(Union Types)表示取值可以为多种类型中的一种。
    2. let myFavoriteNumber: string | number;
    3. myFavoriteNumber = 'seven';
    4. myFavoriteNumber = 7;
    5. // 当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,我们只能访问此联合类型的所有类型里共有的属性或方法
    6. function getLength(something: string | number): number {
    7. return something.length;
    8. }
    9. // index.ts(2,22): error TS2339: Property 'length' does not exist on type 'string | number'.
    10. // Property 'length' does not exist on type 'number'.
    11. // 此时的情况可以使用类型保护来进行解决(1.typeof 2.instanceof 3.in 4.字面量类型保护 5.使用定义的类型保护)
  • 对象的类型——接口 ``` // 在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement) interface Person { name: string; age: number; } let tom: Person = { name: ‘Tom’, age: 25 }; // 有时我们希望不要完全匹配一个形状,那么可以用可选属性: interface Person { name: string; age?: number; } let tom: Person = { name: ‘Tom’ }; // 有时候我们希望一个接口允许有任意的属性,可以使用如下方式 // 需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集 interface Person { name: string; age?: number; [propName: string]: string | number; } let tom: Person = { name: ‘Tom’, gender: ‘male’ }; // 有时候我们希望对象中的一些字段只能在创建的时候被赋值,那么可以用 readonly 定义只读属性: interface Person { readonly id: number; name: string; age?: number;

  1. [propName: string]: any;

} let tom: Person = { id: 89757, name: ‘Tom’, gender: ‘male’ }; tom.id = 9527; // index.ts(14,5): error TS2540: Cannot assign to ‘id’ because it is a constant or a read-only property.

  1. - 数组的类型

// 最简单的方法是使用「类型 + 方括号」来表示数组: let fibonacci: number[] = [1, 1, 2, 3, 5]; // 数组的项中不允许出现其他的类型: let fibonacci: number[] = [1, ‘1’, 2, 3, 5]; // Type ‘string’ is not assignable to type ‘number’. // 数组的一些方法的参数也会根据数组在定义时约定的类型进行限制: let fibonacci: number[] = [1, 1, 2, 3, 5]; fibonacci.push(‘8’); // Argument of type ‘“8”‘ is not assignable to parameter of type ‘number’. // 我们也可以使用数组泛型(Array Generic) Array 来表示数组: let fibonacci: Array = [1, 1, 2, 3, 5]; // 接口也可以用来描述数组: interface NumberArray {

  1. [index: number]: number;

} let fibonacci: NumberArray = [1, 1, 2, 3, 5]; // 类数组(Array-like Object)不是数组类型,比如 arguments: function sum() { let args: number[] = arguments; } // Type ‘IArguments’ is missing the following properties from type ‘number[]’: pop, push, concat, join, and 24 more. // 事实上常用的类数组都有自己的接口定义,如 IArguments, NodeList, HTMLCollection 等

  1. - 函数的类型

// 一个函数有输入和输出,要在 TypeScript 中对其进行约束,需要把输入和输出都考虑到,其中函数声明的类型定义较简单: function sum(x: number, y: number): number { return x + y; } // 函数表达式 let mySum: (x: number, y: number) => number = function (x: number, y: number): number { return x + y; }; // 注意不要混淆了 TypeScript 中的 => 和 ES6 中的 =>。 // 在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型。在 ES6 中,=> 叫做箭头函数 // 与接口中的可选属性类似,我们用 ? 表示可选的参数: function buildName(firstName: string, lastName?: string) { if (lastName) { return firstName + ‘ ‘ + lastName; } else { return firstName; } } let tomcat = buildName(‘Tom’, ‘Cat’); let tom = buildName(‘Tom’); // 需要注意的是,可选参数必须接在必需参数后面。换句话说,可选参数后面不允许再出现必需参数了 // 在 ES6 中,我们允许给函数的参数添加默认值,TypeScript 会将添加了默认值的参数识别为可选参数: function buildName(firstName: string, lastName: string = ‘Cat’) { return firstName + ‘ ‘ + lastName; } let tomcat = buildName(‘Tom’, ‘Cat’); let tom = buildName(‘Tom’); // 此时就不受「可选参数必须接在必需参数后面」的限制了 // 可以使用 …rest 的方式获取函数中的剩余参数(rest 参数): function push(array, …items) { items.forEach(function(item) { array.push(item); }); } let a: any[] = []; push(a, 1, 2, 3); // 重载允许一个函数接受不同数量或类型的参数时,作出不同的处理。 function reverse(x: number): number; function reverse(x: string): string; function reverse(x: number | string): number | string { if (typeof x === ‘number’) { return Number(x.toString().split(‘’).reverse().join(‘’)); } else if (typeof x === ‘string’) { return x.split(‘’).reverse().join(‘’); } }

  1. - 类型断言

// 类型断言(Type Assertion)可以用来手动指定一个值的类型。 // 值 as 类型 或 <类型>值

  1. - 声明文件当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。语法:
  2. - declare var 声明全局变量<br />
  3. - declare function 声明全局方法<br />
  4. - declare class 声明全局类<br />
  5. - declare enum 声明全局枚举类型<br />
  6. - declare namespace 声明(含有子属性的)全局对象<br />
  7. - interface type 声明全局类型<br />
  8. - export 导出变量<br />
  9. - export namespace 导出(含有子属性的)对象<br />
  10. - export default ES6 默认导出<br />
  11. - export = commonjs 导出模块<br />
  12. - export as namespace UMD 库声明全局变量<br />
  13. - declare global 扩展全局变量<br />
  14. - declare module 扩展模块<br />
  15. - /// <reference /> 三斜线指令<br />
  16. - 内置对象

// ECMAScript 标准提供的内置对象有:Boolean、Error、Date、RegExp 等。 // 我们可以在 TypeScript 中将变量定义为这些类型: let b: Boolean = new Boolean(1); let e: Error = new Error(‘Error occurred’); let d: Date = new Date(); let r: RegExp = /[a-z]/; // DOM 和 BOM 提供的内置对象有:Document、HTMLElement、Event、NodeList 等。 let body: HTMLElement = document.body; let allDiv: NodeList = document.querySelectorAll(‘div’); document.addEventListener(‘click’, function(e: MouseEvent) { // Do something }); ```

5. 学习文章与资料

1.TypeScript 官网
2.深入理解 TypeScript