TypeScript 入门学习笔记

js ts

TypeScript 是 JavaScript 的一个超集,主要提供了类型系统和对 ES6 的支持,它由 Microsoft 开发,代码开源于 GitHub 上。

参考书籍:TypeScript 入门教程

在线compiler

安装 TypeScript

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

npm install -g typescript
1

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

tsc hello.ts
1

我们约定使用 TypeScript 编写的文件以 .ts 为后缀,用 TypeScript 编写 React 时,以 .tsx 为后缀。

数据类型

JavaScript 的类型分为两种:原始数据类型(Primitive data types)和对象类型(Object types)。

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

布尔值、数值、字符串

前四种类型就是在参数名称后面使用冒号来指定参数的类型。

let isDone: boolean = false;
let decLiteral: number = 6;
let hexLiteral: number = 0xf00d;
let myName: string = 'Tom';
let myAge: number = 25;
// 模板字符串
let sentence: string = `Hello, my name is ${myName}.
I'll be ${myAge + 1} years old next month.`;
1
2
3
4
5
6
7
8

空值

JavaScript 没有空值(Void)的概念,在 TypeScript 中,可以用 void 表示没有任何返回值的函数:

function alertName(): void {
    alert('My name is Tom');
}
1
2
3

声明一个 void 类型的变量没有什么用,因为你只能将它赋值为 undefinednull:

let unusable: void = undefined;
1

void 不同,undefinednull 是所有类型的子类型。也就是说 undefined 类型的变量,可以赋值给 number 类型的变量,但 void 类型的变量不能赋值给 number 类型的变量。

任意值类型

any 类型,允许被赋值为任意类型。

let myFavoriteNumber: any = 'seven';
myFavoriteNumber = 7;
1
2

注意:

  • 声明一个变量为任意值之后,对它的任何操作,返回的内容的类型都是任意值。
  • 变量如果在声明的时候,未指定其类型,那么它会被识别为任意值类型。
  • 如果没有明确的指定类型,那么 TypeScript 会依照类型推论(Type Inference)的规则推断出一个类型。

联合类型

联合类型(Union Types)表示取值可以为多种类型中的一种。使用 | 分隔每个类型。

let myFavoriteNumber: string | number;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
1
2
3

注意:只能访问此联合类型的所有类型里共有的属性或方法。

对象的类型--接口(Interfaces)

在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型。

在面向对象语言中,接口(Interfaces)是一个很重要的概念,它是对行为的抽象,而具体如何行动需要由类(classes)去实现(implement)。

TypeScript 中的接口是一个非常灵活的概念,除了可用于对类的一部分行为进行抽象以外,也常用于对「对象的形状(Shape)」进行描述。

简单的例子

interface Person {
    name: string;
    age: number;
}

let tom: Person = {
    name: 'Tom',
    age: 25
};
1
2
3
4
5
6
7
8
9

上面的例子中,我们定义了一个接口 Person,接着定义了一个变量 tom,它的类型是 Person。这样,我们就约束了 tom 的形状必须和接口 Person 一致。

接口一般首字母大写。有的编程语言中会建议接口的名称加上 I 前缀。

注意:赋值的时候,变量的形状必须和接口的形状保持一致。定义的变量比接口多了或少了一些属性是不允许的。

可选属性、任意属性、只读属性

// ?表示属性是可选的,可选属性的含义是该属性可以不存在。仍然不允许添加未定义的属性。
// [propName: string]: any 表示允许 obj[xxx] 这样的动态属性
// readonly 表示只读,不能对其属性进行重新赋值
interface Person {
    readonly id: number;
    name: string;
    age?: number;
    [propName: string]: any; // 使用 [propName: string] 定义了任意属性取 string 类型的值。
}

let tom: Person = {
    id: 89757,
    name: 'Tom',
    gender: 'male'
};

// 不允许对只读属性 id 重新赋值,如:tom.id = 9527
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

数组类型

// 最简单的方法是使用「类型 + 方括号」来表示数组:
let fibonacci: number[] = [1, 1, 2, 3, 5];

// 数组的项中不允许出现其他的类型:
let fibonacci: number[] = [1, '1', 2, 3, 5];

// 也可以使用数组泛型(Array Generic) `Array<elemType>` 来表示数组:
let fibonacci: Array<number> = [1, 1, 2, 3, 5];

// 一个比较常见的做法是,用 `any` 表示数组中允许出现任意类型
let list: any[] = ['xcatliu', 25, { website: 'http://xcatliu.com' }];
1
2
3
4
5
6
7
8
9
10
11

类数组(Array-like Object)不是数组类型,比如 arguments

类数组都有自己的接口定义,如 IArguments, NodeList, HTMLCollection 等:

function sum() {
    let args: IArguments = arguments;
}
1
2
3

函数类型

在 JavaScript 中,有两种常见的定义函数的方式——函数声明(Function Declaration)和函数表达式(Function Expression)。

一个函数有输入和输出,要在 TypeScript 中对其进行约束,需要把输入和输出都考虑到。

// 函数声明
function sum(x: number, y: number): number {
    return x + y;
}

// 函数表达式
// 注意不要混淆了 TypeScript 中的 => 和 ES6 中的 =>。
// 在 TypeScript 的类型定义中,=> 用来表示函数的定义,左边是输入类型,需要用括号括起来,右边是输出类型
let mySum: (x: number, y: number) => number = function (x: number, y: number): number {
    return x + y;
};

// 用接口定义函数的形状,可添加参数默认值,可选参数用 ? 表示
interface SearchFunc {
    (source: string, key: string = 'Tom', subString?: string): boolean;
}

let mySearch: SearchFunc;
mySearch = function(source: string, key: string = 'Tom', subString?: string) {
    return source.search(subString) !== -1;
}

// 剩余参数用数组的类型来定义
function push(array: any[], ...items: any[]) {
    items.forEach(function(item) {
        array.push(item);
    });
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

注意: - 输入多余的(或者少于要求的)参数,是不被允许的。 - 可选参数后面不允许再出现必需参数了。

类型断言

语法:<类型>值值 as 类型

在 tsx 语法(React 的 jsx 语法的 ts 版)中必须用后一种。

// 将一个联合类型的变量指定为一个更加具体的类型
function getLength(something: string | number): number {
    if ((<string>something).length) {  // number 类型无 .length 属性,所以需要断言 something 为 string 属性,否则会报错
        return (<string>something).length;
    } else {
        return something.toString().length;
    }
}
1
2
3
4
5
6
7
8

注意: 类型断言不是类型转换,断言成一个联合类型中不存在的类型是不允许的。

声明文件

当使用第三方库时,我们需要引用它的声明文件,才能获得对应的代码补全、接口提示等功能。

新语法索引

  • declare var 声明全局变量
  • declare function 声明全局方法
  • declare class 声明全局类
  • declare enum 声明全局枚举类型
  • declare namespace 声明(含有子属性的)全局对象
  • interfacetype 声明全局类型
  • export 导出变量
  • export namespace 导出(含有子属性的)对象
  • export default ES6 默认导出
  • export = commonjs 导出模块
  • export as namespace UMD 库声明全局变量
  • declare global 扩展全局变量
  • declare module 扩展模块
  • /// <reference /> 三斜线指令

什么是声明文件

通常我们会把声明语句放到一个单独的文件(jQuery.d.ts)中,这就是声明文件

声明文件必需以 .d.ts 为后缀。

一般来说,ts 会解析项目中所有的 *.ts 文件,当然也包含以 .d.ts 结尾的文件。所以当我们将 jQuery.d.ts 放到项目中时,其他所有 *.ts 文件就都可以获得 jQuery 的类型定义了。

目录
/path/to/project
├── src
|  ├── index.ts
|  └── jQuery.d.ts
└── tsconfig.json
1
2
3
4
5
6
// src/jQuery.d.ts
declare var jQuery: (selector: string) => any;

// src/index.ts
jQuery('#foo');
1
2
3
4
5

假如仍然无法解析,那么可以检查下 tsconfig.json 中的 files、include 和 exclude 配置,确保其包含了 jQuery.d.ts 文件。

上次更新: 9/28/2019, 1:37:47 PM