QX-AI
GPT-4
QX-AI初始化中...
暂无预设简介,请点击下方AI实时简介按钮。
介绍自己
生成本文简介
推荐相关文章
AI实时简介

初识

TypeScript 是由巨硬开发的 JS 的超集,通过添加可选的静态类型和基于类的面向对象编程思想,使得代码更易于理解、维护和重构。此外,TS 还支持其他高级语言功能,如接口、泛型、命名空间和装饰器等

规范:TS 提供了更好的类型检查和代码提示工具,并且可以使用第三方库中的类型声明文件进行代码补全和类型检查

开发:TS 为现代Web应用程序和大型项目提供了一个强大的编程环境,可以帮助开发人员减少错误、提高开发效率和代码质量

运行:TS 可以转换成 JS 并在任何支持 JS 的环境中运行,包括浏览器、Node.js和移动应用程序等

安装:npm install typescript -g 会在全局暴露一个 tsc 命令

tsc -init 初始化,创建 tsconfig.json

tsc -w 监视所有 TS 文件变化,实时编译成 JS

npm install ts-node -g 安装 ts-node 更方便地运行 TS

配置文件

tsconfig.json 是 TS 的配置文件

可以定义哪些文件需要包含在编译过程中、使用哪个ECMAScript目标版本、生成哪种模块系统、是否开启严格模式等等

常用配置项:

  1. target 设置编译后的 JavaScript 代码使用的 ECMAScript 版本,默认为 ES3,可设置为 ES5、ES6/ES2015、ES7 等。
  2. strict 开启所有严格类型检查选项,包括noImplicitAny,noImplicitThis,alwaysStrict等等。如果希望 TypeScript 给出尽可能多的类型检查错误提示,可以将此选项设置为 true。
  3. module 指定生成的模块规范类型,可选值有 commonjs、amd、system 和 es2015。
  4. lib 指定编译过程中需要引入的库文件,默认情况下只包含 DOM 和 ES 标准库,可以通过指定其他库来获得更好的类型检查支持。
  5. moduleResolution 指定模块解析策略,可选值有 classic、node、和 yarn pnp。
  6. esModuleInterop 简化导入cjs模块功能
  7. outDir 输出目录,指定编译后的 JavaScript 代码所在的目录。
  8. rootDir 项目根目录
  9. allowJs 允许编译器编译 .js 文件(通常是为了容易迁移现有的 JavaScript 项目到 TypeScript 项目)
  10. sourceMap 生成 sourcemap 文件,方便调试 TypeScript 代码时能够正确地映射回原始的 TypeScript 代码。
  11. noImplicitAny 当 TypeScript 无法推导出变量的类型时,给出一个错误提醒
  12. noImplicitThis 禁止 this 关键字的隐式 any 类型
  13. strictNullChecks 严格模式下对 null 和 undefined 的检查。

TS检查相关注释

(1) // @ts-nocheck 加到文件首行,当前文件不需要 ts 校验
(2) // @ts-check 加到文件首行,对当前文件进行 ts 校验
(3) // @ts-ignore 忽略下一行代码的 ts 校验
(4) // eslint-disable-next-line 忽略下一行代码的 eslint 校验
(5) /* eslint-disable */ eslint忽略

类型标注

通过 <数据>:<类型> 对变量、函数返回值、函数参数等数据进行类型的标注、限制

思想:定义任何东西的时候要注明类型,调用任何东西的时候要检查类型。

可以标注的类型:
(1) 基础类型:Boolean、Number、String、null、undefined 以及 ES6 的 Symbol 和 ES10 的 BigInt。
(2) 空值:Void
(3) 顶级类型:任意类型 Any 和 不知道的类型 Unknown
(4) Object、object 和 {}
(5) 接口和对象类型:interface定义
(6) 数组类型
(7) 函数类型

基本类型

1、字符串类型:string

1
2
3
let str: string = "chuckle";
str = `1+1=${1+1}`; // 可以使用模板字符串
str = "1+1=" + "2"; // 可以拼接

2、数字类型:number

1
2
3
4
5
let num: number = 1
num = NaN
num = Infinity
num = 0xf00d // 十六进制
num = 0b1010 // 二进制

3、布尔类型:boolean

注意:new Boolean() 返回的是一个 Boolean 对象,而不是布尔值

1
2
let bool: boolean = true
bool = Boolean(0) // false

4、Null 和 undefined 类型

undefined 和 null 是所有类型的子类型,在非严格模式下可以赋给其它任何类型的变量

严格模式下:
(1) undefined 和 null 不能赋给其它类型的变量,但 undefined 可以赋给 void 类型(一般也用不到)
(2) null 和 undefined 类型也不能相互赋值

1
2
3
4
5
6
let a: null = null
let b: undefined = undefined
a = b // 严格模式下不允许
let str: string = a // 严格模式下不允许
let c: void = null // 严格模式下不允许
c = undefined // 始终允许

空值void

JS 中没有空值的概念,TS 中可以使用 void 表示函数无返回值

注意:
(1) 不能将 void 赋给除 any 外其它类型的变量。
(2) 使用 any 类型的变量接收函数返回的空值,打印 undefined
(3) Boolean(void) 值是 false

1
2
3
4
5
6
function fun(): void{}
let a: any = fun();
console.log(a); // undefined
a = Boolean(fun());
console.log(a); // false
let b: string = fun() // 不允许

any和unknown

any 和 unknown 是 TS 中的顶级类型,可以包含所有类型的数据

如果所有变量都是 any 类型,那就是 AnyScript,写起来和 JS 没什么区别,最好不要这么做

1
2
3
4
let a: any = 1;
a = "qx"
a = true
a = null

any 与 unknown 的区别:
any 可以赋给其它类型,而 unknown 不能赋值给除 any 和 unknown 以外的其它类型

1
2
3
4
5
let a: any = 1
let b: unknown = 2
let num: number = a // any可以赋给其它类型
a = b // unknown可以赋给any
num = b // unknown不能赋给其它类型

unknown 类型不能读任何元素、属性,也不能调用任何方法,所以 unknown 比 any 安全

1
2
3
4
5
let u: unknown = { a: 1 }
console.log(u); // 可以
console.log(u.a); // 错误
u = ()=>{ console.log('@@') }
u() // 错误

Object、object和{}

Object、object 和 {} 虽然能保存的数据类型不同,但都和 unknown 类型一样,不能读任何元素、属性,也不能调用任何方法

1、Object 类型是所有 Object 类的实例的类型,字面量 {} 代表的也是 Object

由于原型链顶层就是 Object,所有基本数据类型和引用类型最终都指向 Object,所以他也包含所有类型

1
2
3
4
5
let obj: Object = 1;
obj = "chuckle"
console.log(obj); // chuckle
obj = { a: 1 }
console.log(obj.a); // 报错:类型Object上不存在属性a

2、object 代表所有非值类型的类型(数组、对象、函数)等,常用于泛型约束

1
2
3
4
5
6
let o: object = ()=>{ console.log('@@') }
o() // 此表达式不可调用。类型{}没有调用签名
o = ['1','2']
console.log(o[1]) // 错误
o = { a: 1 }
console.log(o) // 报错:类型object上不存在属性a

接口和对象类型

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

即使用 interface 来定义一种对对象的约束

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

1
2
3
4
5
6
7
8
9
10
11
// 定义一个对象
interface Person{
name: String,
age: number,
}
// 必须按照定义的形状来声明
let person: Person = {
name: "chuckle",
age: 20
}

1、重名的接口会合并,共同起作用

1
2
3
4
5
6
7
8
9
10
11
interface Person{
name: String,
}
interface Person{
age: number,
}
let person: Person = {
name: "chuckle",
age: 20
}

2、任意属性 [propName: string] (索引签名)

声明的对象中可以有任意个数所定义的类型(或其子类型)的属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface Person{
name: String,
age: number,
[propName: string]: any
}
let person: Person = {
name: "chuckle",
age: 20,
// 接下来可以有任意个数any类型的属性
a: 1,
b: "qx",
c(){
console.log(this.a)
}
}

3、readonly 让属性只读

1
2
3
4
interface Person{
readonly name: String,
age: number,
}

4、定义对象中的函数

1
2
3
interface Person{
fun: (value:number)=>number
}

6、接口继承 extends

1
2
3
4
5
6
7
8
9
10
11
interface A{
name: String,
}
interface B extends A{
age: number,
}
let b: B = {
name: "chuckle",
age: 20,
}

数组类型

使用 :<元素类型>[] 来定义一个数组

1
let arr: number[] // 定义数值类型的数组

使用泛型的方式定义

1
let arr: Array<number>

定义二维数组

1
2
let arr: number[][]
let arr: Array<Array<number>>

定义包含多种类型的数组

1
let arr: (number|string)[] = [1, 2, '3', 4]

元组:元素的类型、位置、个数必须一一对应

1
let arr: [number,string] = [1,'qx']

函数类型与定义

定义一个函数类型变量

1
2
let fun: Function
fun = ()=>{}

定义函数的参数和返回值

1
2
3
4
function fun(a:number, b:number):number{
return a+b;
}
console.log(fun(1,1))

使用 interface 定义函数类型(函数也是一种对象)

1
2
3
4
5
6
7
8
9
10
interface Fn {
// 只有一个string类型的参数,返回值是数值类型
(name: string): number
}
let fun: Fn = (name)=>{
console.log(name)
return 1
}
fun("chuckle")

TS 可以定义函数中的 this 类型,用于增强补全,必须写在函数的第一个参数上,不作为真正的参数定义

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
interface Obj{
name: string
// TS可以定义this的类型(一般用于增强补全),必须在第一个参数上定义
add: (this:Obj, num:number)=>void
}
let obj:Obj = {
name: "chuckle",
add(num:number){
console.log(num);
console.log(this.name);
// 此时this.就会有补全
}
}
obj.add(1);

?可选符

使用 ?: 定义函数参数或对象属性作为可选项

1
2
3
4
5
6
7
8
9
interface Person{
name: String,
age?: number, // 年龄可选
}
// 第二个b参数可选
function fun(a:number, b?:number):number{
return b ? a+b : a;
}

联合类型

使用 <类型1>|<类型2> 标注多个类型

1
2
3
4
// 函数可传入两种参数
function fun(a: number | string) { }
// 可保存两种类型的变量
let a: number | string

交叉类型

<类型1>&<类型2> 需同时满足两种类型,通常配合接口使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface Student {
name: string
id: number
}
interface Person {
name: string
age: number
}
let p: Student & Person = {
name: "chuckle",
id: 1,
age: 20
}

类型断言

值 as 类型<类型>值 欺骗 TypeScript 编译器,但无法避免运行时的错误

1
2
3
4
5
6
function fun(a: number | string){
// console.log(a.length); // 数值类型没.length方法,会报错
console.log((<string>a).length); // 断言a一定是string类型
}
fun(123) // undefined
fun('123') // 3

TS 直接在 window 上添加属性是不允许的,但可以将 window 断言为 any 类型,就可以添加属性了

1
2
3
window.a = 1; // 不可以
(<any>window).a = 1; // 断言成any就可以了
console.log((<any>window).a);

内置对象

JS 中有很多内置对象,它们可以直接在 TS 中当做定义好了的类型来使用

1、ECMAScript 的内置对象:内置对象名就是类型名

1
2
3
4
5
6
7
8
9
10
11
12
13
let b: Boolean = new Boolean(1)
console.log(b)
let n: Number = new Number(true)
console.log(n)
let s: String = new String('chuckle')
console.log(s)
let d: Date = new Date()
console.log(d)
let r: RegExp = /^123/
console.log(r)
let e: Error = new Error("error")
console.log(e)

2、DOM 和 BOM 的内置对象:
HTML<标签名>Element 某个内置标签的类型,input、span 等
HTMLElement 语义化的标签名,footer、header,以及自定义标签名
Element 任何标签元素的类型
NodeList 任何元素集合的类型
NodeListOf<其它类型> 指定元素集合的类型

由于元素可能获取不到,要加上 null 组成联合类型

1
2
3
4
5
6
let div1: HTMLInputElement | null = document.querySelector('input');
let div2: HTMLDivElement | null = document.querySelector('div');
let div3: NodeListOf<HTMLElement> | null = document.querySelectorAll('.content');
let local: string | null = localStorage.getItem('token')
let lct: Location = location
let pms: Promise<number> = new Promise((r)=>r(1))
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//dom元素的映射表
interface HTMLElementTagNameMap {
"a": HTMLAnchorElement;
"abbr": HTMLElement;
"address": HTMLElement;
"applet": HTMLAppletElement;
"area": HTMLAreaElement;
"article": HTMLElement;
"aside": HTMLElement;
"audio": HTMLAudioElement;
"b": HTMLElement;
"base": HTMLBaseElement;
"bdi": HTMLElement;
"bdo": HTMLElement;
"blockquote": HTMLQuoteElement;
"body": HTMLBodyElement;
"br": HTMLBRElement;
"button": HTMLButtonElement;
"canvas": HTMLCanvasElement;
"caption": HTMLTableCaptionElement;
"cite": HTMLElement;
"code": HTMLElement;
"col": HTMLTableColElement;
"colgroup": HTMLTableColElement;
"data": HTMLDataElement;
"datalist": HTMLDataListElement;
"dd": HTMLElement;
"del": HTMLModElement;
"details": HTMLDetailsElement;
"dfn": HTMLElement;
"dialog": HTMLDialogElement;
"dir": HTMLDirectoryElement;
"div": HTMLDivElement;
"dl": HTMLDListElement;
"dt": HTMLElement;
"em": HTMLElement;
"embed": HTMLEmbedElement;
"fieldset": HTMLFieldSetElement;
"figcaption": HTMLElement;
"figure": HTMLElement;
"font": HTMLFontElement;
"footer": HTMLElement;
"form": HTMLFormElement;
"frame": HTMLFrameElement;
"frameset": HTMLFrameSetElement;
"h1": HTMLHeadingElement;
"h2": HTMLHeadingElement;
"h3": HTMLHeadingElement;
"h4": HTMLHeadingElement;
"h5": HTMLHeadingElement;
"h6": HTMLHeadingElement;
"head": HTMLHeadElement;
"header": HTMLElement;
"hgroup": HTMLElement;
"hr": HTMLHRElement;
"html": HTMLHtmlElement;
"i": HTMLElement;
"iframe": HTMLIFrameElement;
"img": HTMLImageElement;
"input": HTMLInputElement;
"ins": HTMLModElement;
"kbd": HTMLElement;
"label": HTMLLabelElement;
"legend": HTMLLegendElement;
"li": HTMLLIElement;
"link": HTMLLinkElement;
"main": HTMLElement;
"map": HTMLMapElement;
"mark": HTMLElement;
"marquee": HTMLMarqueeElement;
"menu": HTMLMenuElement;
"meta": HTMLMetaElement;
"meter": HTMLMeterElement;
"nav": HTMLElement;
"noscript": HTMLElement;
"object": HTMLObjectElement;
"ol": HTMLOListElement;
"optgroup": HTMLOptGroupElement;
"option": HTMLOptionElement;
"output": HTMLOutputElement;
"p": HTMLParagraphElement;
"param": HTMLParamElement;
"picture": HTMLPictureElement;
"pre": HTMLPreElement;
"progress": HTMLProgressElement;
"q": HTMLQuoteElement;
"rp": HTMLElement;
"rt": HTMLElement;
"ruby": HTMLElement;
"s": HTMLElement;
"samp": HTMLElement;
"script": HTMLScriptElement;
"section": HTMLElement;
"select": HTMLSelectElement;
"slot": HTMLSlotElement;
"small": HTMLElement;
"source": HTMLSourceElement;
"span": HTMLSpanElement;
"strong": HTMLElement;
"style": HTMLStyleElement;
"sub": HTMLElement;
"summary": HTMLElement;
"sup": HTMLElement;
"table": HTMLTableElement;
"tbody": HTMLTableSectionElement;
"td": HTMLTableDataCellElement;
"template": HTMLTemplateElement;
"textarea": HTMLTextAreaElement;
"tfoot": HTMLTableSectionElement;
"th": HTMLTableHeaderCellElement;
"thead": HTMLTableSectionElement;
"time": HTMLTimeElement;
"title": HTMLTitleElement;
"tr": HTMLTableRowElement;
"track": HTMLTrackElement;
"u": HTMLElement;
"ul": HTMLUListElement;
"var": HTMLElement;
"video": HTMLVideoElement;
"wbr": HTMLElement;
}

Class