【笔记】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

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

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
const 变量名: 数据类型 = 值;
  • 自动类型推导,常量的数据类型为字面量类型
1
const 变量名 = 值;

基本数据类型

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

const 变量名: string = "";

const 变量名: boolean = false;

const 变量名: null = null;

const 变量名: undefined = undefined;

进制表示

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

数组类型

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

定义数组元素的数据类型

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

元组类型

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

定义元祖元素的数据类型

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

对象类型

定义空对象

1
const 变量名: object = {};
1
const 变量名: {} = {};

定义对象属性的数据类型

1
2
3
const 变量名: { 属性名: 数据类型 } = {
属性名: 值
}
  • 自动类型推导,属性的数据类型为any类型
1
2
3
const 变量名: { 属性名 } = {
属性名: 值
}
  • 对于非首次定义的对象,这个对象的属性个数无需匹配
1
2
let a: { c: string, d: string } = { c: "", d: "" };
let b: { c: string } = a;

多个属性

1
2
3
4
const 变量名: { 属性名1: 数据类型, 属性名2: 数据类型 } = {
属性名1: 值,
属性名2: 值
}
多行定义属性
1
2
3
4
5
6
7
const 变量名: {
属性名1: 数据类型,
属性名2: 数据类型
} = {
属性名1: 值,
属性名2: 值
}
  • 如果多行定义属性,可以省略,
1
2
3
4
5
6
7
const 变量名: {
属性名1: 数据类型
属性名2: 数据类型
} = {
属性名1: 值,
属性名2: 值
}

可选的属性

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

只读的属性

1
2
3
4
5
const 变量名: {
readonly 属性名: 数据类型
} = {
属性名: 值
}

函数作为对象属性

传送门

函数类型

定义函数

通过函数声明定义函数

1
function 函数名() {}

通过函数表达式定义函数

  • 通过函数表达式定义函数时,需要在类型中定义函数类型表达式
1
const 函数名: () => void = function() {};
  • 自动类型推导
1
const 函数名 = function() {};

定义形参的数据类型

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

多个形参

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 fn(k = "default") {
console.log(k);
}

fn(); // "default"
fn(undefined); // "default"

函数作为实参

  • 函数作为其他函数的实参时,这个函数的形参最好直接数据类型推导,因为在其他函数已经定义了这个函数的形参类型
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") {
const 变量名: never = 形参名;
}
}

函数调用签名

  • 如果一个对象的属性是函数类型,可以通过_函数调用签名_的方式定义数据类型

声明

1
2
3
4
interface 接口名 {
属性名: 属性数据类型
(形参名: 形参数据类型): 返回值类型
}

定义

1
2
3
4
const 变量名: 接口名 = function(形参名: 形参数据类型): 返回值类型 {
return 返回值;
}
变量名.属性名 = 属性值;

调用

1
2
console.log(变量名(实参值)); // 返回值
console.log(变量名.属性名); // 属性值

构造函数调用签名

  • 如果一个对象的属性是构造函数类型,可以通过_构造函数调用签名_的方式定义数据类型

声明

1
2
3
4
interface 接口名 {
属性名: 属性数据类型
new (形参名: 形参数据类型): 返回值类型
}

定义

1
2
3
4
const 变量名: 接口名 = function(形参名: 形参数据类型): 对象类型 {
return 对象;
} as unknown as 接口名;
变量名.属性名 = 属性值;

调用

1
2
console.log(new 变量名(实参值)); // 对象
console.log(变量名.属性名); // 属性值

函数的重载

  1. 编写重载签名
  2. 编写通用的实现
1
2
3
4
5
function fn(arg: string)
function fn(arg: number)
function fn(arg: any) {
console.log(arg);
}

字面量类型

1
const 变量名: 值 = 值;

任意类型

1
const 变量名: 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
const 变量名: 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
const 变量名: 数据类型 | 数据类型;

交叉类型

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

类型别名

声明类型别名

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

使用类型别名

1
const 变量名: 类型别名;

类型守卫

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

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
interface 接口名 {
属性名: 数据类型
}

可选的属性

1
2
3
interface 接口名 {
属性名?: 数据类型
}

使用接口

1
const 变量名: 接口名;

通过交叉类型使用多个接口

1
const 变量名: 接口名1 & 接口名2;

通过同名接口补充接口定义的属性

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

interface 接口名 {
属性名2: 数据类型
}

继承

1
2
3
4
5
6
7
interface Father {
属性名1: 数据类型
}

interface Son extends Father {
属性名2: 数据类型
}

声明类

1
2
3
class 类名 {
属性名: 数据类型
}

实现接口

1
2
3
class 类名 implements 接口名 {
属性名: 数据类型
}

完成