开始

TypeScript 是 JavaScript 的超集,扩展了 JavaScript 的语法,在 JavaScript 的基础上增加了编译期的类型检查

全局安装 TypeScript 命令:npm i -g typescript

TypeScript 需要先编译为 JavaScript 才能工作,编译命令为 tsc xxx.ts

类型

类型声明

TS 中声明变量,在变量名后加上类型

1
let a: number = 10;

若变量在声明时初始化,则进行类型推断,若未初始化且未声明类型,则默认为 Any 类型

1
2
3
4
// 未初始化且未声明类型,默认类型为Any
let a;
a = 10;
a = "Hello";

TS 支持字面量类型声明

1
let a: 10;  // a的值只能是10

类型声明支持同时声明多个类型(联合类型)

1
2
let a: number | string;  // a只能是number类型或string类型
let b: "a" | "b"; // 配合字面量类型声明实现枚举

函数类型声明

  • 支持参数类型声明
  • 支持返回值类型声明
  • 支持限定参数个数
1
2
3
function func(a: number, b: number): number {
return a + b;
}

基本类型

  • number

  • string

  • boolean

  • any:任意类型

  • unknown:类型转换安全的 any,在赋值给其他类型时要显式类型检查

    1
    2
    3
    4
    5
    6
    7
    8
    let a: unknown;
    let s: string;
    if (typeof a === "string") {
    s = a;
    }

    // 强制类型转换
    s = a as string;
  • void:函数无返回值

  • never:函数永远不会返回,类似 kotlin 的 nothing

  • object:对象类型

    一个对象类型是通过属性名和属性类型确定

    1
    let obj: { name: string, age: number }

    支持可选属性

    1
    let obj: { name: string, age?: number }

    动态任意属性

    1
    2
    // property为任意属性名,属性声明为any类型
    let obj: { name: string, [property: string]: any }

    不同对象的属性混合

    1
    2
    // obj是个既有name属性又有age属性的对象类型
    let obj: { name: string } & { age: number };
  • 函数类型

    使用类似 kotlin 的语法声明一个函数类型

    1
    2
    3
    4
    5
    6
    7
    8
    let f: (a: number, b: number) => number;

    // 函数赋值
    function fun(a: number, b: number): number {
    return a + b;
    }
    f = fun;
    f = (a, b) => a + b; // 使用lambda表达式
  • array

    1
    2
    let arr: number[] = [1, 2, 3, 4];
    let arr1: Array<number> = [1, 2, 3, 4];
  • tuple:看做固定长度数组

    1
    let t: [string, number] = ["Hello", 123];
  • enum:枚举类型

    1
    2
    3
    4
    enum Gender {
    Male,
    Female
    }

类型别名

1
2
type MyType = string;
type MyType1 = 1 | 2 | 3;

tsc 编译

使用 tsc --init 命令创建 tsconfig.json 文件,用于配置 tsc 编译

默认配置如下

1
2
3
4
5
6
7
8
9
10
{
"compilerOptions": {
"target": "es2016",
"module": "commonjs",
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"strict": true,
"skipLibCheck": true
}
}

根对象配置

配置名 描述
include: [] 选择被编译的文件
exclude: [] 选择排除编译的文件

compilerOptions 配置

配置名 描述
target 指定编译的 JS 版本
module 指定模块化方案
lib:[] 指定项目使用的库,一般不自定义设置
outDir 指定编译输出目录
outFile 将编译结果输出为一个文件
allowJs 是否允许编译 js 文件
checkJs 是否检查 js 代码
removeComments 是否去除注释
noEmit 不输出编译文件
noEmitOnError 当编译错误时,不输出编译文件
alwaysStrict 编译结果是否使用严格模式
noImplicitAny 是否不允许隐式 any 类型
noImplicitThis 是否不允许不明确 this
strictNullChecks 是否严格空检查
strict 是否启用所有严格检查

面向对象

与 js 类相似,基本声明如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Person {

// 实例字段
name: string;
age: number = 20;

// 静态字段
static count: number = 0;

// 只读字段
readonly hello: number = 10;

// 构造器
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}

// 实例方法
say() {
// ...
}
}

字段封装

  • 字段支持访问级别设置:public、private、protected

  • getter 与 setter 方法与 js 相同

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    // 可提供getter和setter
    getName() {
    return this.name
    }
    setName(name) {
    this.name = name
    }
    // 简化语法糖,可简化为属性访问形式
    get name() {
    return this.name
    }
    set name(name) {
    this.name = name
    }

继承

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class Animal {
name: string = "";

sayHello() {
// ...
}
}

class Dog extends Animal {
// 字段子类重写
name: string = "Hello";

constructor() {
// 父类构造
super();
}

// 方法子类重写
sayHello() {
// ...
super.sayHello(); // 通过super引用父类
}
}

抽象类

1
2
3
4
5
6
7
abstract class Person {
// 抽象字段
abstract name: string;

// 抽象方法
abstract say(): void;
}

接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface MyInterface {
// 接口字段
name: string;

// 接口方法
method(): void;
}

// 实现接口
class Person implements MyInterface {
name: string = "";

method(): void {
}
}

泛型

泛型函数

泛型函数声明

1
2
3
function func<T>(a: T): T {
return a;
}

限定泛型参数上界

1
2
3
function func<T extends A>() {
// ...
}

泛型类

1
2
3
4
5
6
class Person<T> {
name: T;
constructor(name: T) {
this.name = name;
}
}

索引签名

可以通过字符串来访问对象的属性

1
2
3
4
5
6
const obj: any = {
value: "test"
}
obj["value1"] = "test1"

console.log(obj["value"])

在 typescript 中,索引签名必须是 stringnumber 类型

显式指定索引签名

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const foo: {
[index: string]: { message: string };
} = {};

// 储存的东西必须符合结构
// ok
foo['a'] = { message: 'some message' };

// Error, 必须包含 `message`
foo['a'] = { messages: 'some message' };

// 读取时,也会有类型检查
// ok
foo['a'].message;

// Error: messages 不存在
foo['a'].messages;

与字段声明结合使用

1
2
3
4
5
6
7
8
9
10
11
12
13
// ok
interface Foo {
[key: string]: number;
x: number;
y: number;
}

// Error
interface Bar {
[key: string]: number;
x: number;
y: string; // Error: y 属性必须为 number 类型
}