TypeScript

类型系统

  • 在开发过程中找错
  • 使用”类型注解“来分析代码
  • 仅存在于开发阶段
  • 不会提供性能优化

类型

  • 一个更方便我们去更简便地描述一个具有相应的属性和方法的值的东西

  • 我们能够赋值给变量(variable)的东西
  • 每一个值都会有相应的类型

类型的分类

  • 元类型(primitive types)

    • string、number、boolean、undefined、null、void、symbol
  • 对象类型(Object types)

    • function、arrays、classes、objects
  • 基础类型:Boolean,Number,String,null,undefined 和 ES6的Symbol和ES10的BigInt

字符串类型

1
2
3
4
5
//普通声明
let a:string = 'CNY'z

//也可以使用es6的字符串模板
let muban:string = `CNY{a}`

数字类型

  • 支持十六进制、十进制、八进制和二进制
1
2
3
4
5
6
7
let notANumber: number = NaN;//Nan
let num: number = 123;//普通数字
let infinityNumber: number = Infinity;//无穷大
let decimal: number = 6;//十进制
let hex: number = 0xf00d;//十六进制
let binary: number = 0b1010;//二进制
let octal: number = 0o744;//八进制s

布尔类型

  • 使用构造函数Boolean创造的对象不是布尔值
1
2
let createdBoolean: boolean = new Boolean(1)
//这样会报错 应为事实上 new Boolean() 返回的是一个 Boolean 对象
  • 事实上 new Boolean() 返回的是一个 Boolean 对象 需要改成
1
2
3
4
let createdBoolean: Boolean = new Boolean(1)

let booleand: boolean = true //可以直接使用布尔值
let booleand2: boolean = Boolean(1) //也可以通过函数返回布尔值

空值类型

  • JavaScript 没有空值(Void)的概念,在 TypeScript 中,可以用 void 表示没有任何返回值的函数
1
2
3
function voidFn(): void {
console.log('test void')
}
  • void类型的用法,主要体现在我们不希望调用者关心函数返回值的情况下,比如通常的异步函数

  • void也可以定义undefined和null类型

1
2
let u: void = undefined
let n: void = null
  • 如果tsconfig.json 开启了严格模式,null 不能赋值给void

Null和undefined类型

1
2
let u: undefined = undefined //定义undefined
let n: null = null //定义null
  • void 和 undefined 和 null 最大的区别

  • 与void的区别是,undefined 和 null 是所以类型的子类型。也就是说undefined类型的变量,可以赋值给string类型的变量

1
2
3
4
5
//这样写会报错 void 类型不可以分给其他类型
let test: void = undefined
let num2: string = "CNY"

num2 = test
1
2
3
4
5
6
7
8
9
10
11
//这样是没问题的
let test: null = null
let num2: string = "1"

num2 = test

//或者这样的
let test: undefined = undefined
let num2: string = "1"

num2 = test

Any类型

  • 没有强制限定哪种类型,随时切换类型都可以 我们可以对 any 进行任何操作,不需要检查类型
1
2
3
let anys:any = 123
anys = '123'
anys = true
  • 声明变量的时候没有指定任意类型默认为any
1
2
3
let anys;
anys = '123'
anys = true
  • 弊端如果使用any 就失去了TS类型检测的作用

unkonw类型

  • unknow类型比any更加严格当你要使用any 的时候可以尝试使用unknow
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
//unknown 可以定义任何类型的值
let value: unknown;

value = true; // OK
value = 42; // OK
value = "Hello World"; // OK
value = []; // OK
value = {}; // OK
value = null; // OK
value = undefined; // OK
value = Symbol("type"); // OK

//这样写会报错unknow类型不能作为子类型只能作为父类型 any可以作为父类型和子类型
//unknown类型不能赋值给其他类型
let names:unknown = '123'
let names2:string = names

//这样就没问题 any类型是可以的
let names:any = '123'
let names2:string = names

//unknown可赋值对象只有unknown 和 any
let bbb:unknown = '123'
let aaa:any= '456'

aaa = bbb
1
2
3
4
5
6
7
8
9
//如果是any类型在对象没有这个属性的时候还在获取是不会报错的
let obj:any = {b:1}
obj.a


//如果是unknow 是不能调用属性和方法
let obj:unknown = {b:1,ccc:():number=>213}
obj.b
obj.ccc()

对象类型

  • interface(接口)来定义一种约束
1
2
3
4
5
6
7
8
9
10
11
//使用接口约束的时候不能多一个属性也不能少一个属性
//必须与接口保持一致

interface Person{
b:string,
a:string
}
const person:Person = {
a:'123',
b:'123'
}
1
2
3
4
5
6
7
8
9
10
11
//重名interface 可以合并
interface A {
name:string
}
interface A {
age:string
}
let obj:A ={
name:'CNY',
age:'22'
}
1
2
3
4
5
6
7
8
9
10
11
12
//继承
interface A{
name:string,
}
interface B extends A{
age:string
}

let obj:B = {
name:'CNY',
age:"22"
}
1
2
3
4
5
6
7
8
9
//可选属性 使用?操作符
//可选属性的含义是该属性可以不存在
interface Person{
b?:string,
a:string
}
const person:Person = {
a:"23",
}
  • 一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集
1
2
3
4
5
6
7
8
9
10
11
12
//任意属性[propName:string]
//允许添加新的任意属性
//一个接口中只能定义一个任意属性。如果接口中有多个类型的属性,则可以在任意属性中使用联合类型:
interface Person{
[propName:string]:string | number,
a:string
}

const person:Person = {
a:'CNY',
b:'22'
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//只读属性readonly
//readonly只读属性是不允许被赋值的只能读取

interface Person{
b?:string,
readonly a:string,
[propName:string]:any
}

const person:Person = {
a:'123',
c:'123'
}

//person.a = 123 只读属性无法重新赋值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
//添加函数
interface Person {
b?:string,
readonly a:string,
[propName:string]:any,
fn():void
}
const person:Person = {
a:'123',
b:'123',
fn:()=>{
console.log(123)
}
}

数组类型

1
2
3
4
5
6
7
8
9
10
11
12
//类型加中括号
let arr:number[] = [123]
//这样会报错定义了数字类型出现字符串是不允许的
let arr:number[] = [1,2,3,'1']
//操作方法添加也是不允许的
let arr:number[] = [1,2,3,]
arr.unshift('1')


var arr: number[] = [1, 2, 3]; //数字类型的数组
var arr2: string[] = ["1", "2"]; //字符串类型的数组
var arr3: any[] = [1, "2", true]; //任意类型的数组
1
2
3
4
5
6
7
8
9
10
//最简单的方法是使用「类型 + 方括号」来表示数组:
let fibonacci: number[] = [1, 1, 2, 3, 5];

//数组的项中不允许出现其他的类型:
let fibonacci: number[] = [1, '1', 2, 3, 5]; //error

//数组的一些方法的参数也会根据数组在定义时约定的类型进行限制:
let fibonacci: number[] = [1, 1, 2, 3, 5];
fibonacci.push('8'); //error

1
2
//数组泛型
let arr:Array<number> = [1,2,3,4,5]
1
2
3
4
5
6
7
//用接口表示数组
//一般来描述类数组
interface NumberArr {
[index:number]:number;
}
let fibonacci: NumberArr = [1,2,3,4,5,6]
//表示:只要索引的类型是数字时,那么值的类型必须是数字
1
2
//any 在数组中的应用
let list: any[] = ['test', 1, [],{a:1}]

函数的类型

1
2
3
4
5
//注意,参数不能多传,也不能少传 必须按照约定的类型来
const fn = (name: string, age:number): string => {
return name + age
}
fn('张三',18)
1
2
3
4
5
//通过?表示该参数为可选参数
const fn = (name: string, age?:number): string => {
return name + age
}
fn('张三')
1
2
3
4
const fn = (name: string = "我是默认值"): string => {
return name
}
fn()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
//定义参数 num 和 num2  :后面定义返回值的类型
interface Add {
(num: number, num2: number): number
}

const fn: Add = (num: number, num2: number): number => {
return num + num2
}
fn(5, 5)


interface User{
name: string;
age: number;
}
function getUserInfo(user: User): User {
return user
}
1
2
3
4
5
6
7
8
const fn = (array:number[],...items:any[]):any[] => {
console.log(array,items)
return items
}

let a:number[] = [1,2,3]

fn(a,'4','5','6')
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
//函数重载

//重载是方法名字相同,而参数不同,返回类型可以相同也可以不同。

//如果参数类型不同,则参数类型应设置为 any。

//参数数量不同你可以将不同的参数设置为可选。

function fn(params: number): void

function fn(params: string, params2: number): void

function fn(params: any, params2?: any): void {

console.log(params)

console.log(params2)

}



fn(123)

fn('123',456)

联合类型

1
2
3
4
let myPhone: number | string  = '010-820'

//这样写是会报错的应为我们的联合类型只有数字和字符串并没有布尔值
let myPhone: number | string = true
1
2
3
4
//函数使用联合类型
const fn = (something:number | boolean):boolean => {
return !!something
}

交叉类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//多种类型的集合,联合对象将具有所联合类型的所有成员

interface People {
age: number,
height: number
}
interface Man{
sex: string
}
const CNY = (man: People & Man) => {
console.log(man.age)
console.log(man.height)
console.log(man.sex)
}
CNY({age: 18,height: 180,sex: 'male'});

类型断言

值 as 类型 <类型>值

1
2
3
4
5
6
7
8
9
10
11
12
interface A {
run: string
}

interface B {
build: string
}

const fn = (type: A | B): string => {
return type.run
}
//这样写是有警告的应为B的接口上面是没有定义run这个属性的
1
2
3
4
5
6
7
8
9
10
11
12
interface A {
run: string
}

interface B {
build: string
}

const fn = (type: A | B): string => {
return (type as A).run
}
//可以使用类型断言来推断他传入的是A接口的值
  • 类型断言只能够「欺骗」TypeScript 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误
1
2
(window as any).abc = 123
//可以使用any临时断言在 any 类型的变量上,访问任何属性都是允许的。

内置对象

1
2
3
4
5
6
7
8
9
10
11
12
let b: Boolean = new Boolean(1)
console.log(b)
let n: Number = new Number(true)
console.log(n)
let s: String = new String('CNY')
console.log(s)
let d: Date = new Date()
console.log(d)
let r: RegExp = /^1/
console.log(r)
let e: Error = new Error("error!")
console.log(e)
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
123
124
125
126
127
128
let body: HTMLElement = document.body;
let allDiv: NodeList = document.querySelectorAll('div');
//读取div 这种需要类型断言 或者加个判断应为读不到返回null
let div:HTMLElement = document.querySelector('div') as HTMLDivElement
document.addEventListener('click', function (e: MouseEvent) {

});
//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类

1
2
3
4
5
6
7
8
class Person{
constructor () {

}
run () {

}
}
  • 在TypeScript中是不允许直接在constructor中直接定义变量的,需要在constructor上面先声明
1
2
3
4
5
6
7
class Person {
name:string,
age:number
constructor (name:string,age:number){
this.name = name
}
}
  • 这样引发了第二个问题,你如果定义了变量不用,也会报错,通常是给个默认值,或者是进行赋值
1
2
3
4
5
6
7
8
class Person {
name:string,
age:number = 0
constructor (name:string,age:number){
this.name = name
this.age = age
}
}
  • 类的修饰符
    • public private protected
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
class Person {
public name:string,
private age:number
protected some:any
static sth:string
constructor (name:string,age:number,some:any){
this.name = name
this.age = age
this.any = any
//this.sth error
}
static run () {
return '1
}
static go() {
return this.run()
}
}

let cny = new Person('CNY',22,'N')
cny.name
cny.age
cny.some
Person.sth
Person.run()

class Man extends Person{
constructor () {
this.some
}
}

使用public修饰符 可以让你所定义的变量,内部访问,也可以外部访问,如果不写默认就是public

使用private修饰符 代表定义的变量是私有的,只能在内部访问,不能在外部访问

使用protect修饰符 代表定义的变量是私有的,只能在内部和继承的子类中访问,不能在外部访问

static静态属性和静态方法

用static定义的属性,不可以通过this去访问,只能通过类名去调用

同理static静态函数,也不能通过this去调用,也只能通过类名去调用

如果两个函数都是static静态函数,可以通过this相互调用

  • interface 定义类
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
interface PersonClass {
get(type: boolean): boolean
}

interface PersonClass2{
set():void,
asd:string
}

class A {
name: string
constructor() {
this.name = "123"
}
}

class Person extends A implements PersonClass,PersonClass2 {
asd: string
constructor() {
super()
this.asd = '123'
}
get(type:boolean) {
return type
}
set () {

}
}

抽象类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
abstract class A {
name:string
constructor (name:string) {
this.name = name
}
abstract getName():string
}

class B extends A {
constructor () {

}
getName():string{
return 'CNY'
}
}

元组

  • 如果需要一个固定大小的不同类型值的集合
  • 元组就是数组的变种
  • 元组(Tuple)就是固定数量的不同类型的元素的组合
  • 元组与集合的不同之处在于,元组中的元素类型可以是不同的,而且数量固定,元组的好处在于可以把多个元素作为一个单元传递,如果一个方法需要返回多个值,可以把这多个值作为元组返回,而不需要创建额外的类来表示
1
2
3
4
let arr:[number,string] = [1,'string']


let arr2: readonly [number,boolean,string,undefined] = [1,true,'sring',undefined]
1
2
3
4
5
6
7
8
//应用场景 例如定义excel返回的数据
let excel: [string, string, number, string][] = [
['title', 'name', 1, '123'],
['title', 'name', 1, '123'],
['title', 'name', 1, '123'],
['title', 'name', 1, '123'],
['title', 'name', 1, '123'],
]

枚举类型

1
2
3
4
5
6
enum Types{
Red = 0,
Green = 1,
BLue = 2
}
//默认就是从0开始的 可以不写值

增长枚举

1
2
3
4
5
6
//定义了一个数字枚举, Red使用初始化为 1。 其余的成员会从 1开始自动增长
enum Types{
Red = 1,
Green,
BLue
}

字符串枚举

1
2
3
4
5
6
//在一个字符串枚举里,每个成员都必须用字符串字面量
enum Types{
Red = 'red',
Green = 'green',
BLue = 'blue'
}

异构枚举

1
2
3
4
5
//枚举可以混合字符串和数字成员
enum Types{
No = "No",
Yes = 1,
}

接口枚举

1
2
3
4
5
6
7
8
9
10
11
enum Types {
C,
D
}
interface A {
red:Types.C
}

let obj:A = {
red:Types.C
}

const枚举

1
2
3
4
5
6
7
8
//let  和 var 都是不允许的声明只能使用const
//大多数情况下,枚举是十分有效的方案。 然而在某些情况下需求很严格。 为了避免在额外生成的代码上的开销和额外的非直接的对枚举成员的访问,我们可以使用 const枚举。 常量枚举通过在枚举上使用 const修饰符来定义
//const 声明的枚举会被编译成常量

const enum Types{
No = "No",
Yes = 1,
}

反向映射

1
2
3
4
5
6
7
8
9
//它包含了正向映射( name -> value)和反向映射( value -> name)
//不会为字符串枚举成员生成反向映射

enum A {
CNY
}
let c = A.CNY
console.log(c) //0
let c1 = A[0] //CNY

类型推断

  • 当变量声明和变量初始化在同一行的时候,类型推断系统才会起作用
  • 手动添加类型注解的三种情况
1
2
3
// 1)变量声明和变量初始化不在同一行
// 2)当一个函数返回any类型,但我们想要明确具体类型
// 3)一个变量拥有一个不能推断出来的类型