TypeScript-Learning-1

本文最后更新于:2024年7月2日 晚上

环境搭建与初步使用

首先需要先安装Node.js(这边顺便推荐管理多版本Node.js的神器nvm),然后执行命令:npm install -g typescript
安装完毕后,可以使用tsc --version来检查 TS 当前版本。
在文件夹下使用tsc --init,可以创建tsconfig.json配置文件。使用tsc filename.ts命令可输出对应的js文件。

类型系统

TS 具有类型系统,可以进行类型推导,例如:let varibleA = "A";,TS 能够推导出此变量的类型为string

类型注解

我们可以主动提供变量的类型给 TS (或者说,将变量的类型告诉 TS ),例如:let variableA: string;

模块

ECMAScript 2015 增加了“ECMAScript模块”(ESM),提供了在文件之间共享代码的标准句法,即:importexport
举个例子,以下模块文件从同级文件./values中导入value,并导出doubled变量:

1
2
import { value } from "./values";
export const doubled = value * 2;

在一个模块文件(顶层有exportimport语句的文件)中声明的变量,与另一个文件中声明的同名变量没有名称冲突(除非从其他文件中导入同名变量)。
而对于脚本文件(模块之外的文件)而言,TS 采用全局作用域,即在一个脚本文件中声明的变量不能与另一个脚本文件中的变量同名。

联合类型与字面量类型

首先明白两个概念:

  1. 联合:扩充值的类型,变成两个或更多可能的类型。
  2. 收窄:缩小值的类型,不能是一个或多个可能的类型。

联合类型

可以手动声明联合类型:let unionTypeExample: string | null | underfined
TS 只允许访问联合类型中各成员共有的成员属性。

收窄

通过逻辑检查收窄类型的过程叫做类型守卫(type guard)。下面介绍几种常用的类型守卫方式。

赋值收窄

直接为变量赋值,TS 将收窄变量的类型,变成所赋值的类型。

1
2
3
4
5
6
7
let typeGuardExample: number | string;

typeGuardExample = "Hello World";

typeGuardExample.toUpperCase(); // Works

typeGuardExample.toFixed(); // Error

条件检查

if语句中,检查变量的值是否等于某个已知值,TS 能够收窄变量类型。

1
2
3
4
5
6
7
let typeGuardExample = Math.random() > 0.5 ? "Hello World" : 1;

if (typeGuardExample === "Hello World"){
typeGuardExample.toUpperCase();
}

typeGuardExample.toUpperCase(); // Error

typeof检查

除了直接检查值,TS 也能利用typeof运算符收窄变量类型。

1
2
3
4
5
6
7
8
9
10
let typeGuardExample = Math.random() > 0.5 ? "Hello World" : 1;

if (typeof typeGuardExample === "string"){
typeGuardExample.toUpperCase();
}
else{
typeGuardExample.toFixed();
}

typeof typeGuardExample === "string" ? typeGuardExample.toUpperCase() : typeGuardExample.toFixed();

属性检查

还可以通过检查一个属性是否存在于类型中,来收窄变量类型。if ("pages" in poem)检查poem变量中是否包括了pages属性。

1
2
3
4
5
if ("pages" in poem){
poem.pages;
} else {
poem.rhymes;
}

字面量类型

确知一个值的类型是原始类型的一个具体值,而不是原始类型可以取的其他任何值,这种类型就叫字面量类型(literal type)。使用const声明变量,而且指定一个字面量值,TS 推导出的类型就是那个字面量值。

真值检查

TS 编译器中的strictNullChecks选项控制是否开启严格空值检查。

类型别名(type aliase)

1
2
3
4
type IdMaybe = Id | undefined | null;
type Id = number | string;

let myId: IdMaybe;

也可以编写对象类型别名:

1
2
3
4
5
6
type Poet = {
born: number,
name: string,
};

let poetLater: Poet;

结构类型

TS 类型系统属于结构类型(structural typing):满足一个类型的值均可用作那个类型的值。看下面的例子会更直观:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
type WithFirstName = {
firstName: string;
}

type WithLastName = {
lastName: string;
}

const hasBoth = {
firstName: 'John',
lastName: 'Doe',
}

let withFirstName = hasBoth; // Ok
let withLastName = hasBoth; // Ok

超额属性检查

使用对象类型声明的变量,如果初始值包含的字段比类型描述的多,TS 将报告类型错误。

1
2
3
4
let extraProeprty: WithFirstName = {
firstName: 'John',
lastName: 'Doe', // Error
};

注意,仅当创建满足对象类型的对象字面量时才触发超额属性检查。提供现有的对象字面量跳过此项检查。

可选属性

对象不是必须包含对象类型描述的所有属性。在属性的类型注解中,在:前面加上?,表示属性是可选的。

1
2
3
4
5
6
7
8
type Book = {
title: string;
author?: string;
};

let book1: Book = {
title: "Harry Potter",
};

可区分联合

通过对象的一个属性指明对象的形状,这种类型形状叫做可区分联合(discriminated union),指明对象类型的属性叫作判别属性(discriminant)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type PoemWithPages = {
name: string;
pages: number;
type: 'pages';
};

type PoemWithRhymes = {
name: string;
rhymes: boolean;
type: 'rhymes';
};

type Poem = PoemWithPages | PoemWithRhymes;

const poem: Poem = Math.random() > 0.5 ? { name = "1", pages = 1, type: "pages" } : { name = "1", rhymes = true, type: "rhymes" };

if (poem.type === "pages") {
console.log(`${poem.pages}`);
} else {
console.log(`${poem.rhymes}`);
}

poem.type;

交叉类型

TS 使用&表示一个类型同时满足多个类型,这叫做交叉类型(intersection type)

1
2
3
4
5
6
7
8
9
10
11
type ArtWork = {
genre: string;
name: string;
};

type Writing = {
pages: string;
name: string;
};

type WrittenArt = ArtWork & Writing;

这里有一只爱丽丝

希望本文章能够帮到您~


TypeScript-Learning-1
https://map1e-g.github.io/2024/06/30/TypeScript-Learning-1/
作者
MaP1e-G
发布于
2024年6月30日
许可协议