【笔记】TypeScript学习笔记

前言

TypeScript是由微软进行开发和维护的一种开源的编程语言。TypeScript是JavaScript的严格语法超集,提供了可选的静态类型检查。(维基百科

TypeScript本质上是JavaScript的超集,所以JavaScript的所有用法都可以在TypeScript上直接使用,本文只介绍TypeScript的特殊用法

编译器

下载依赖

1
npm install -g typescript

编译生成JS文件

-w:监视模式,每当源码发生改变就立即执行编译

编译指定文件

1
tsc <file>.ts
1
tsc <file_1>.ts <file_2>.ts

根据配置文件编译指定文件

  • 初始化配置文件
1
tsc --init
  • 修改配置文件

compilerOptions.outDir:指定输出目录
include:指定包含的文件或目录
exclude:指定排除的文件或目录

tsconfig.json
1
2
3
4
5
6
7
{
"compilerOptions": {
"outDir": "./",
},
"include": ["./"],
"exclude": ["./dist"]
}
1
tsc

变量

声明变量

1
let 变量名: 数据类型;
  • 自动类型推导,声明变量时的数据类型为any类型
1
let 变量名;

定义变量

1
let 变量名: 数据类型 = 值;
  • 自动类型推导,定义变量时的数据类型根据值的类型决定
1
let 变量名 = 值;

常量

定义常量

1
let 变量名: 数据类型 = 值;
  • 自动类型推导,常量的数据类型为字面量类型
1
let 变量名 = 值;

基本数据类型

1
2
3
4
5
6
7
8
9
let 变量名: number = 0;

let 变量名: string = "";

let 变量名: boolean = false;

let 变量名: null = null;

let 变量名: undefined = undefined;

进制表示

  • 描述二进制的数值,需要以0b开头
1
var binary = 0b1;
  • 描述八进制的数值,需要以0o开头
1
var octal = 0o7;
  • 描述十六进制的数值,需要以0x开头
1
var hexadecimal = 0xF;

数组类型

  • 数组中只能存放相同类型的元素

定义数组元素的数据类型

1
let 变量名: 数据类型[] = [值, 值];

元组类型

  • 元组中可以存放不同类型的元素

定义元祖元素的数据类型

1
let 变量名: [数据类型, 数据类型] = [值, 值];

对象类型

定义对象数据类型

1
2
3
let 变量名: object = {
属性名: 值
};
1
2
3
let 变量名: {} = {
属性名: 值
};

定义对象属性的数据类型

1
2
3
let 变量名: { 属性名: 数据类型 } = {
属性名: 值
};
1
2
3
4
let 变量名: { 属性名1: 数据类型, 属性名2: 数据类型 } = {
属性名1: 值,
属性名2: 值
};
  • 如果通过多行定义多个属性,可以省略,
1
2
3
4
5
6
7
let 变量名: {
属性名1: 数据类型
属性名2: 数据类型
} = {
属性名1: 值,
属性名2: 值
};
  • 自动类型推导,属性的数据类型为any类型
1
2
3
let 变量名: { 属性名 } = {
属性名: 值
};

可选的属性

  • 通过?定义可选的属性
1
2
3
let 变量名: { 属性名?: 数据类型 } = {
属性名: 值
};
  • 可选的对象属性的数据类型实质上是这个数据类型与undefined类型的联合类型(数据类型 | undefined
1
2
3
let 变量名: { 属性名?: 数据类型 | undefined } = {
属性名: 值
};

只读的属性

  • 通过权限修饰符readonly定义只读的属性
1
2
3
let 变量名: { readonly 属性名: 数据类型 } = {
属性名: 值
};

严格字面量赋值检测

  • 首次给类型为非空对象的变量赋值时,TypeScript采用严格字面量赋值检测,此时要求对象属性与类型定义的接口完全相同,不能出现多余属性
  • 如果不是首次给变量赋值,TypeScript不会采用严格字面量赋值检测,可以出现多余属性
1
2
3
4
5
6
let 变量名1: { 属性名1: 数据类型, 属性名2: 数据类型 } = {
属性名1: 值,
属性名2: 值
};

let 变量名2: { 属性名1: 数据类型 } = 变量名1;

对象所有属性的联合类型

1
2
3
4
5
6
interface 接口名 {
属性名1: 数据类型;
属性名2: 数据类型;
}

let 变量名: keyof 接口名;
  • 相当于对象所有属性的联合类型
1
2
3
4
5
6
interface 接口名 {
属性名1: 数据类型;
属性名2: 数据类型;
}

let 变量名: "属性名1" | "属性名2";

函数类型

定义函数数据类型

通过函数声明定义函数

1
function 函数名() {}

通过函数表达式定义函数

1
let 函数名: () => void = function () {};
  • 自动类型推导
1
let 函数名 = function () {};

定义形参的数据类型

1
function 函数名(形参名: 数据类型) {}
1
function 函数名(形参名1: 数据类型, 形参名2: 数据类型) {}
  • 自动类型推导,形参的数据类型为any类型
1
function 函数名(形参名) {}

不定长形参

1
function 函数名(...形参名: 数据类型[]) {}

可选的形参

  • 通过?定义可选的形参
1
function 函数名(形参名?: 数据类型) {}
  • 可选的函数形参的数据类型实质上是这个数据类型与undefined类型的联合类型(数据类型 | undefined
1
function 函数名(形参名?: 数据类型 | undefined) {}
  • 函数具有多个形参时,可选的形参应该放在末尾
1
function 函数名(形参名1: 数据类型, 形参名2?: 数据类型) {}

形参的默认值

1
function 函数名(形参名: 数据类型 = 默认值) {}
  • 数据类型推导
1
function 函数名(形参名 = 默认值) {}
  • 有默认值的形参可以不传递实参,也可以接收unedfined值作为实参,最终实参的值都是默认值
1
2
3
4
5
6
function 函数名(形参名 = "默认值") {
console.log(形参名);
}

函数名(); // "默认值"
函数名(undefined); // "默认值"

函数作为实参

  • 函数作为其他函数的实参时,这个函数的形参最好直接数据类型推导,因为在其他函数已经定义了这个函数的形参类型
1
2
3
function 函数名(f: (形参名: 数据类型) => void) {}

函数名(function (形参名) {});
  • 函数作为其他函数的实参时,这个函数的形参个数无需匹配
1
2
3
function 函数名(f: (形参名1: 数据类型, 形参名2: 数据类型) => void) {}

函数名(function (形参名1) {});

定义返回值的数据类型

1
2
3
function 函数名(): 数据类型 {
return 返回值;
}
  • 自动类型推导,返回值的数据类型为void类型
1
function 函数名() {}

空类型

  • void类型用于描述一个函数没有返回值
1
function 函数名(): void {}
  • 如果如果函数的返回值类型为void,返回值也可以是undefined
1
2
3
function 函数名(): void {
return undefined;
}
  • 如果函数的返回值类型void是由上下文推导而得来的,那么不强制要求_不返回任何数据或返回undefined,否则必须_不返回任何数据或返回undefined

永无类型

  • never类型可以用于描述一个函数永远不会成功得到返回值,比如:死循环、抛出异常
1
2
3
function 函数名(): never {
while (true);
}
1
2
3
function 函数名(): never {
throw new Error();
}
  • never类型也可以用于描述不应当被赋值为这个类型的变量,如果被赋值为这个类型的变量,则会编译期报错
1
2
3
4
5
function 函数名(形参名: 数据类型) {
if (typeof 形参名 === "string") {
let 变量名: never = 形参名;
}
}

函数的重载

  1. 编写重载签名
  2. 编写通用的实现
  3. 调用重载函数,而不要调用通用函数
1
2
3
4
5
6
7
8
function fn(arg1: string, arg2: string)
function fn(arg1: number, arg2: number)
function fn(arg1: any, arg1: any) {
console.log(arg1 + arg2);
}

fn(0, 0); // 0
fn("0", "0"); // "00"

字面量类型

1
let 变量名: 值 = 值;

任意类型

1
let 变量名: any = 值;
  • any类型的变量可以赋值给非any类型的变量,所以污染原本有数据类型的变量,这个变量的数据类型最终会根据传递的数据重新自动推导
1
2
3
4
5
let a: any = "";
let b: number;

b = a;
console.log(typeof b); // "string"
  • any类型的变量可以直接调用属性和方法
1
2
3
let a: any = "";

console.log(a.length);

未知类型

1
let 变量名: unknown = 值;
  • unknow类型的变量不可以赋值给非unknow类型的变量,所以不会污染原本有数据类型的变量
1
2
3
4
5
6
let a: unknown;
let b: number;
let c: unknown;

// b = a;
c = a;
  • unknown类型的变量不可以直接操作任意属性和方法,但是可以通过数据类型转换,从而调用属性和方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
let a: unknown = "";
let b: string;

// console.log(a.length);

if (typeof a === "string") {
b = a;
console.log(b.length);
}

b = a as string;
console.log(b.length);

b = <string>a;
console.log(b.length);

联合类型

  • 只要满足一种数据类型就可以匹配成功
1
let 变量名: 数据类型 | 数据类型;

交叉类型

  • 必须满足两种数据类型才可以匹配成功
    • 如果是非类或接口之间的交叉类型,那么实际为never类型
    • 如果是类或接口之间的交叉类型,那么必须满足两者定义的所有属性才可以匹配成功
1
let 变量名: 数据类型 & 数据类型;

类型别名

定义类型别名

1
type 类型别名 = 数据类型;

使用类型别名

1
let 变量名: 类型别名;

类型守卫

  • 类型守卫可以实现类型缩小

typeof操作符

1
2
3
4
5
6
let a: string | null;
let b: string;

if (typeof a === "string") {
b = a;
}

instanceof操作符

1
2
3
4
5
6
let a: Date | null;
let b: Date;

if (a instanceof Date) {
b = a;
}

in操作符

1
2
3
4
5
6
let a: { key1: string } | { key2: number };
let b: string;

if ("key1" in a) {
b = a.key;
}

类型断言

  • 类型断言可以实现类型缩小,也可以实现类型放大
1
2
3
4
let a: unknown = "";
let b: string;

b = a as string;
1
2
3
4
let a: unknown = "";
let b: string;

b = <string>a;

非空类型断言

  • 告诉编译器,这个变量一定不为空
1
2
3
4
let a: string | null;
let b: string;

b = a!;

常量类型断言

  • 告诉编译器,自动类型推导时,全部按照字面量类型
1
2
3
4
let a = { key: "value" } as const;
let b: "value";

b = a.key;

映射类型

  • 将旧数据类型映射为新数据类型
1
2
3
4
5
type 映射类型<T> = {
[Property in keyof T]: T[Property]
}

type 新数据类型名 = 映射类型<旧数据类型名>

添加或移除修饰符

readonly+readonly:添加readonly修饰符,+为缺省值
?+?:添加?修饰符,+为缺省值

1
2
3
4
5
type 映射类型<T> = {
+readonly [Property in keyof T]+?: T[Property]
}

type 新数据类型名 = 映射类型<旧数据类型名>

-readonly:移除readonly修饰符
-?:移除?修饰符

1
2
3
4
5
type 映射类型<T> = {
-readonly [Property in keyof T]-?: T[Property]
}

type 新数据类型名 = 映射类型<旧数据类型名>

完成