前言:

TS是JS的超集,所以JS基础的类型都包含在内

  • 首先安装 npm install typescript -g

安装完之后,运行tsc -v可以看到当前ts的版本号

  • nodejs环境执行ts文件
  • npm i @types/node –save-dev(node环境支持的依赖必装)
  • npm i ts-node –g

基础类型

基础类型:Boolean、Number、String、nullundefined 以及 ES6 的 Symbol 和 ES10 的 BigInt。

字符串类型

  • 用string来定义,其中可以用`来定义ES6中的模板字符串,${expr}用来表示在模板字符串中的嵌入表达式。
1
2
3
4
5
let str : string = '123'

let str2 : string = `web ${str}`

console.log(str2)

数字类型

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

布尔类型

1
2
3
4
5
6
7
8
9
10
let b : boolean = false

let b2 : boolean = true

let b3 : boolean = Boolean(1)

// 返回一个Boolean对象
let b4 : Boolean = new Boolean(1)

console.log(b)

空值类型

1
2
3
4
5
let n : void = undefined 

let m : void = null // 严格模式下不允许

console.log(n,m) // undefined,null
1
2
3
4
5
6
// 空类型函数,不需要return
function fn() : void{
console.log(123) // 123
}

fn()
  • void 类型的用法,主要是用在我们不希望调用者关心函数返回值的情况下,比如通常的异步回调函数

void和undefined和null的区别

  • void类型不能赋值为undefined
1
2
3
4
5
//这样写会报错 void类型不可以分给其他类型
let test: void = undefined
let str: string = "1"

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

str = test

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

str = test

注意:如果你配置了tsconfig.json 开启了严格模式,null 不能 赋予 void 类型

1
2
3
4
5
{
"compilerOptions":{
"strict": true
}
}

任意类型

  • 没有特定要哪个类型的时候,可以用any进行声明,而且可以随意切换类型
1
2
3
let a : any = 123
a = 'AliveSeven'
a = true
  • 声明变量的时候没有指定任意类型默认为any
1
2
3
4
let a;
a = 123
a = 'Aliving'
a = false

注意:如果使用any 就失去了TS类型检测的作用

TypeScript 3.0中引入的 unknown 类型,它是any类型对应的安全类型,比any更安全。

  • unknown类型比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 a:unknown = '123'
let b:string = a

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

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

aaa = bbb

接口和对象类型

接口

  • 在ts中,我们定义对象的方式要用interface(接口)。
  • 接口的作用:在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到一种限制和规范的作用。
1
2
3
4
5
6
7
8
9
10
11
//这样写是会报错的 因为我们在person定义了a,b但是对象里面缺少b属性
//使用接口约束的时候不能多一个属性也不能少一个属性
//必须与接口保持一致
interface Person {
a:string,
b:string
}

const person:Person = {
a:"213"
}
  • 同名的接口,会合并
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 重名interface  可以合并
interface A {
name : string
}

interface A {
age : number
}

let obj : A = {
name : 'AliveSeven',
age : 20
}

console.log(obj) // { name : 'AliveSeven',age : 20}

继承

  • B接口继承了A接口,那么B接口的对象要有A接口的变量
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//继承
interface A{
name : string
}

interface B extends A{
age : number
}

let obj : B = {
age : 20,
name : "AliveSeven"
}

console.log(obj) // { age: 20, name: 'AliveSeven' }

可选属性:?操作符

  • ?操作符用于声明可有可无的变量
1
2
3
4
5
6
7
8
9
10
//可选属性的含义是该属性可以不存在
//所以说这样写也是没问题的
interface Person {
a:string,
b?:string
}

const person:Person = {
a:"123"
}

任意属性 [propName: string]

注意:一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//在这个例子当中我们看到接口中并没有定义sex但是并没有报错
//应为我们定义了[propName: string]: any;
//允许添加新的任意属性
interface A {
[propName : string] : any
name : string
age? : number
}

let a : A = {
name : 'AliveSeven',
sex : 'boy'
}

console.log(a) // { name : 'AliveSeven',sex : 'boy'}

只读属性 readonly

  • readonly 只读属性是不允许被赋值的只能读取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//这样写是会报错的
//因为sex是只读的不允许重新赋值
interface A {
[propName : string] : any
name : string
age? : number
readonly sex : string // 只读属性
}

let a : A = {
name : 'AliveSeven',
sex : 'boy'
}

a.sex = 'girl'

增加函数

  • 接口里面可以声明函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface A  {
[propName : string] : any
name : string
age? : number
readonly sex : string // 只读属性
exam() : number
}

let a : A = {
name : 'AliveSeven',
sex : 'boy',
exam:() => {
return 100
},
}

console.log(a) // { name: 'AliveSeven', sex: 'boy', exam: [Function: exam] }

数组类型

  • 常见的声明数组方法👇
1
2
3
4
5
6
let a : number[] = [1,2,3]   // 数字类型数组
let b : string[] = ['Alive','Seven'] //字符串类型数组
let c : boolean[] = [true , false] // 布尔类型数组
let d : any[] = [1,2,'Love',true] // 任意类型数组

console.log(a,b,c,d) // [ 1, 2, 3 ] [ 'Alive', 'Seven' ] [ true, false ] [ 1, 2, 'Love', true ]
  • 数组泛型
1
2
3
4
5
6
7
8
9
10
let arr : Array<number> = [66 , 77 , 88]
let str : Array<string> = ['Love' , 'You']
let x : Array<boolean> = [true , false]
let y : Array<any> = [1 , true , 'Fly']

// 多维数组
let z : Array<Array<string | number>> = [[1 , 2 , 'Alive'] , [66 , 'Seven' , '77']]

console.log(arr,str,x,y) // [ 66, 77, 88 ] [ 'Love', 'You' ] [ true, false ] [ 1, true, 'Fly' ]
console.log(z) // [ [ 1, 2, 'Alive' ], [ 66, 'Seven', '77' ] ]

用接口表示数组

1
2
3
4
5
6
7
interface A {
[index : number] : number
}

let a : A = [1,2,3,4]

console.log(a) // [ 1,2,3,4 ]

arguments类数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
function Arr(...args:any): void {
console.log(arguments)
//错误的arguments 是类数组不能这样定义
let arr:number[] = arguments
}
Arr(111, 222, 333)

function Arr(...args:any): void {
console.log(arguments)
//ts内置对象IArguments 定义
let arr:IArguments = arguments
}
Arr(111, 222, 333)

//其中 IArguments 是 TypeScript 中定义好了的类型,它实际上就是:
interface IArguments {
[index: number]: any;
length: number;
callee: Function;
}

函数扩展

函数类型

  • fn = (函数变量1 , 函数变量2 …) : 函数类型 => { 函数返回值 }
1
2
3
4
5
6
7
8
// fn = (函数变量1 , 函数变量2 , 可选函数变量3 ) : 函数类型 => { 函数返回值 }
const fn = (name : string , age : number , sex? : string) : string =>{
return (name + age + sex) // 返回name+age+sex组合字符串
}

let a : string = fn('AliveSeven',20)

console.log(a) // AliveSeven20undefined
  • 可以接口来约束方法变量和类型
1
2
3
4
5
6
7
8
9
10
11
interface A {
name : string
age : number
}

const fn = (a : A) : A =>{
return a
}

let x : A = { name : '步君' , age : 21}
console.log( x ) // { name: '步君', age: 21 }

函数重载

  • 重载是方法名字相同,而参数不同,返回类型可以相同也可以不同。
  • 参数类型不同时,应设置为 any
  • 参数数量不同时,可以将不同的参数设置为可选
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
5
6
//例如我们的手机号通常是13XXXXXXX 为数字类型 这时候产品说需要支持座机
//所以我们就可以使用联合类型支持座机字符串
let myPhone: number | string = '010-820'

//这样写是会报错的因为我们的联合类型只有数字和字符串并没有布尔值
let myPhone: number | string = true
  • 函数使用联合类型:

    1
    2
    3
    const fn = (something:number | boolean):boolean => {
    return !!something
    }

交叉类型

  • 多种类型的集合,联合对象将具有所联合类型的所有成员

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    interface People {
    age: number,
    height: number
    }
    interface Boy{
    sex: string
    }
    const AliveSeven = (boy: People & Boy) => {
    console.log(boy.age)
    console.log(boy.height)
    console.log(boy.sex)
    }

    AliveSeven({age: 21,height: 180,sex: 'male'});
    // 21
    // 180
    // male

类型断言

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接口的值

使用any临时断言

1
2
(window as any).abc = 123
//可以使用any临时断言在 any 类型的变量上,访问任何属性都是允许的。

as const

  • 是对字面值的断言,与const直接定义常量是有区别的

  • 如果是普通类型跟直接const 声明是一样的

1
2
3
4
5
const A = 'AliveSeven'
A = 'aa' //无法修改

let B = 'AliveSeven' as const
B = 'aa' //无法修改
1
2
3
4
5
6
// 数组
let a1 = [10, 20] as const;
const a2 = [10, 20];

a1.unshift(30); // 错误,此时已经断言字面量为[10, 20],数据无法做任何修改
a2.unshift(30); // 通过,没有修改指针

内置对象

ECMAScript 的内置对象

  • Boolean、Number、String、RegExp、Date、Error
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('我是步君')
console.log(s)
let d: Date = new Date()
console.log(d)
let r: RegExp = /^1/
console.log(r)
let e: Error = new Error("错误!")
console.log(e)

DOM 和 BOM 的内置对象

Document、HTMLElement、Event、NodeList等

定义Promise

如果我们不指定返回类型TS是推断不出来返回的是什么类型

指定返回的类型,这里指定为Number

1
2
3
4
5
6
function promise() : Promise<Number>  {
return new Promise<Number>((resolve, reject) => {
resolve(1)
reject(0)
});
}

Class类

在Ts中定义类

1
2
3
4
5
6
7
8
9
10
11
//定义类
class Person {
// 构造函数
constructor () {

}
// 运行函数
run () {

}
}
  • 在TypeScript是不允许直接在constructor 定义变量的 需要在constructor上面先声明

案例:

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
class Person{
// 构造函数
// 默认为public,内外部都能访问
public name : string
// private修饰符,代表定义的变量私有的只能在内部访问,不能在外部访问
private age : number
// protected修饰符,代表定义的变量私有的只能在内部和继承的子类中访问,不能在外部访问
protected sex : any
// 静态属性不需要new,可以直接通过类名调用
static GPA : number = 3.5
constructor(name : string , age : number , sex : any) {
this.name = name
this.age = age
this.sex = sex
}

run(){

}
}

let AliveSeven = new Person('AliveSeven',21,'male')
console.log(AliveSeven.name)
// age只能在Person内部访问,这样写会报错
console.log(AliveSeven.age)
// sex只能在Person类或者其子类里面访问,同样报错
console.log(AliveSeven.sex)

静态属性static

  • 在类中用static关键字声明的变量是静态属性,在constructor不能直接用this去访问,只能通过类名去调用。
  • 同样用static声明的函数也不能用this去调用,也是通过类名调用
  • 如果两个函数都是static 静态的是可以通过this互相调用

完整代码:

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
class Person{
// 构造函数
// 默认为public,内外部都能访问
public name : string
// private修饰符,代表定义的变量私有的只能在内部访问,不能在外部访问
private age : number
// protected修饰符,代表定义的变量私有的只能在内部和继承的子类中访问,不能在外部访问
protected sex : any
// 静态属性不需要new,可以直接通过类名调用
static GPA : number = 3.5
constructor(name : string , age : number , sex : any) {
this.name = name
this.age = age
this.sex = sex
}
// 如果两个函数都是static 静态的是可以通过this互相调用
static run(){
return this.A()
}
static A(){
return 'I am Aliving'
}

}

class Man extends Person {
constructor() {
super('Alive7',22,'male')
// 子类可以访问protected的属性
console.log('继承子类:',this.sex)
}
}

let AliveSeven = new Person('AliveSeven',21,'male')
console.log(AliveSeven.name)
console.log(Person.GPA)

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
30
31
32
33
interface A {
get(type : boolean) :boolean
}

interface B {
set():void
AI : string
}

class C {
name : string
constructor(){
this.name = 'Alive'
}
}

// 类D继承类C,并且实现接口A和B
class D extends C implements A,B{
// 接口B需要AI属性
AI: string
constructor(){
super()
this.AI = 'Aliving'
}
// 接口A有get方法需要实现
get(type: boolean){
return type
}
// 接口B有set方法需要实现
set(){

}
}

抽象类

应用场景如果你写的类实例化之后毫无用处此时我可以把他定义为抽象类

下面这段代码会报错抽象类无法被实例化

实例1:

1
2
3
4
5
6
abstract class A {
public name:string

}

new A()

实例2:

我们在A类定义了 getName 抽象方法但为实现

我们B类实现了A定义的抽象方法,如不实现就不报错,我们定义的抽象方法必须在派生类实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
abstract class A {
name: string
constructor(name: string) {
this.name = name;
}
print(): string {
return this.name
}

abstract getName(): string
}

class B extends A {
constructor() {
super('AliveSeven')
}
getName(): string {
return this.name
}
}

let b = new B();

console.log(b.getName()); // AliveSeven

元组类型

  • 元组(Tuple)是固定数量的不同类型的元素的组合
  • 元组与集合的不同之处在于,元组中的元素类型可以是不同的,而且数量固定。
1
2
3
4
5
6
7
let arr:[number,string] = [1,'string']

let arr2: readonly [number,boolean,string,undefined] = [1,true,'sring',undefined]
// arr[0]的类型
console.log(typeof(arr[0])) // number
// arr[1]的长度
console.log(arr[1].length) // 6

枚举类型

数字枚举

例如:红绿蓝 Red = 0,Green = 1,Blue= 2,分别代表红色0,绿色为1,蓝色为2

1
2
3
4
5
enum Types{
Red,
Green,
BLue
}

这样写就可以实现应为ts定义的枚举中的每一个组员默认都是从0开始

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

增长枚举

1
2
3
4
5
enum Types{
Red = 1,
Green,
BLue
}

如上,我们定义了一个数字枚举, Red使用初始化为 1。 其余的成员会从 1开始自动增长。 换句话说, Type.Red的值为 1Green2Blue3

字符串枚举

  • 字符串枚举的概念很简单。 在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化
1
2
3
4
5
enum Types{
Red = 'red',
Green = 'green',
BLue = 'blue'
}
  • 由于字符串枚举没有自增长的行为,字符串枚举可以很好的序列化。

异构枚举

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

接口枚举

定义一个枚举Types,定义一个接口A,他有一个属性red值为Types.yyds

声明对象的时候要遵循这个规则

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

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

const枚举

let 和 var 都是不允许声明枚举enum的,只能使用const。

大多数情况下,枚举是十分有效的方案。 然而在某些情况下需求很严格,这时我们可以用const枚举

  • const 声明的枚举会被编译成常量
  • 普通声明的枚举编译完后是个对象

反向映射

它包含了正向映射( name -> value)和反向映射( value -> name

1
2
3
4
5
6
7
8
9
enum Types{
A = 1,
B = 2,
C = 1
}
// 根据枚举的值name映射得到value
console.log(Types.A) // 1
// 根据枚举的value映射得到name
console.log(Types[1]) // C

类型推论 | 类型别名

类型推论

我声明了一个变量但是没有定义类型。

TypeScript 会在没有明确的指定类型的时候推测出一个类型,这就是类型推论。

比如我声明一个str变量是’Alive’,所以TS帮我推断出来这是一个string类型。

我们在后面不能再给这个str赋值给别的类型的值。

如果你声明变量没有定义类型也没有赋值这时候TS会推断成any类型可以进行任何操作。

类型别名

type 关键字(可以给一个类型定义一个名字)多用于符合类型

定义类型别名:

定义函数别名

1
2
3
4
5
6
type str = () => string


let s: str = () => "AliveSeven"

console.log(s); // [Function: s]

定义联合类型别名

1
2
3
4
5
6
7
type str = string | number

let s: str = 123

let s2: str = '123'

console.log(s,s2); // 123 123

定义的别名

1
2
3
4
type value = boolean | 0 | '123'

//变量A的值 只能是上面value定义的值
let A:value = true

never类型

TypeScript 将使用 never 类型来表示不应该存在的状态

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// A同时为number和string,这是不可能发生的事情,所以A为never
type A = number & string


// 返回never的函数必须存在无法达到的终点

// 因为存在死循环,所以 loop 将不会有返回值
function loop() : never {
while(true){

}
}

// 因为必定抛出异常,所以 error 将不会有返回值
function error(message: string): never {
throw new Error(message);
}

never 与 void 的差异

1
2
3
4
5
6
7
8
9
// void类型只是没有返回值 但本身不会出错
function Void():void {
console.log('空');
}

// 只会抛出异常没有返回值
function Never():never {
throw new Error('NEVER')
}

应用场景

比如当我们新增了一个C接口,我们必须手动找到所有 switch 代码并处理,否则将有可能引入 BUG。

而且这将是一个“隐蔽型”的BUG,如果回归面不够广,很难发现此类BUG。

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
interface A {
type : 'A类'
}

interface B {
type : 'B类'
}

interface C {
type : 'C类'
}

type All = A | B
function handle(val : All){
switch ( val.type ){
case 'A类' :
break
case 'B类' :
break
default :
// 一般进不来这一步,进来就是程序异常了
const check : never = val
break
}
}

由于任何类型都不能赋值给 never 类型的变量,所以当存在进入 default 分支的可能性时,TS的类型检查会及时帮我们发现这个问题。

Symbol类型

介绍

自ECMAScript 2015起,symbol成为了一种新的原生类型,就像numberstring一样。

symbol类型的值是通过Symbol构造函数创建的。

1
2
3
4
5
let sym1 = Symbol();

let sym2 = Symbol("key"); // 可选的字符串key

console.log(sym2) // Symbol(key)

Symbols是不可改变且唯一的。

1
2
3
4
let sym2 = Symbol("key");
let sym3 = Symbol("key");

sym2 === sym3; // false, symbols是唯一的

用做对象属性的键

1
2
3
4
5
6
7
let sym = Symbol();

let obj = {
[sym]: "value"
};

console.log(obj[sym]); // "value"

symbol属性,不能通过如下方式遍历拿到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
const symbol1 = Symbol('123')
const symbol2 = Symbol('456')
const obj= {
[symbol1]: 'Aliving',
[symbol2]: 'Seven',
age: 21,
sex: 'male'
}
// 1 for in 遍历
for (const key in obj) {
// 注意在console看key,是不是没有遍历到symbol1
console.log(key)
}
// 2 Object.keys 遍历
Object.keys(obj)
console.log(Object.keys(obj))
// 3 getOwnPropertyNames
console.log(Object.getOwnPropertyNames(obj))
// 4 JSON.stringfy
console.log(JSON.stringify(obj))

遍历结果:

1
2
3
4
5
age
sex
[ 'age', 'sex' ]
[ 'age', 'sex' ]
{"age":21,"sex":"male"}

如何拿到:

  1. Object.getOwnPropertySymbols方法
  2. es6 的 Reflect 拿到对象的所有属性
1
2
3
4
5
6
// 1 拿到具体的symbol 属性,对象中有几个就会拿到几个
Object.getOwnPropertySymbols(obj)
console.log(Object.getOwnPropertySymbols(obj))
// 2 es6 的 Reflect 拿到对象的所有属性
Reflect.ownKeys(obj)
console.log(Reflect.ownKeys(obj))

输出结果:

1
2
[ Symbol(123), Symbol(456) ]
[ 'age', 'sex', Symbol(123), Symbol(456) ]

Symbol.iterator 迭代器

1
2
3
4
5
6
7
8
var arr = [1,2,3,4];
let iterator = arr[Symbol.iterator]();

console.log(iterator.next()); //{ value: 1, done: false }
console.log(iterator.next()); //{ value: 2, done: false }
console.log(iterator.next()); //{ value: 3, done: false }
console.log(iterator.next()); //{ value: 4, done: false }
console.log(iterator.next()); //{ value: undefined, done: true }

案例:

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
interface A {
age : number,
name : string
}

// 声明只读数组array
const array : Array<A> = [{ age : 21, name : 'AliveSeven' }, { age : 22 , name : 'Alive' }, { age : 23, name : 'Seven' }]

// 自定义类型
type mapTypes = string | number
const map : Map<mapTypes , mapTypes> = new Map()

map.set('1' , '步君')
map.set('2' , '七海')

const obj = {
a : 123,
b : 456
}

let set : Set<number> = new Set([1,2,3,4,5])

const fn = ( arg : any ) : void => {
let it : Iterator<any> = arg[Symbol.iterator]()
let next : any= { done: false }
while (!next.done) {
next = it.next()
if (!next.done) {
console.log(next.value)
}
}
}

fn(array)
// { age: 21, name: 'AliveSeven' }
// { age: 22, name: 'Alive' }
// { age: 23, name: 'Seven' }

以下为这些symbols的列表:

Symbol.hasInstance
方法,会被instanceof运算符调用。构造器对象用来识别一个对象是否是其实例。

Symbol.isConcatSpreadable
布尔值,表示当在一个对象上调用Array.prototype.concat时,这个对象的数组元素是否可展开。

Symbol.iterator
方法,被for-of语句调用。返回对象的默认迭代器。

Symbol.match
方法,被String.prototype.match调用。正则表达式用来匹配字符串

Symbol.replace
方法,被String.prototype.replace调用。正则表达式用来替换字符串中匹配的子串。

Symbol.search
方法,被String.prototype.search调用。正则表达式返回被匹配部分在字符串中的索引。

Symbol.species
函数值,为一个构造函数。用来创建派生对象。

Symbol.split
方法,被String.prototype.split调用。正则表达式来用分割字符串。

Symbol.toPrimitive
方法,被ToPrimitive抽象操作调用。把对象转换为相应的原始值。

Symbol.toStringTag
方法,被内置方法Object.prototype.toString调用。返回创建对象时默认的字符串描述。

Symbol.unscopables
对象,它自己拥有的属性会被with作用域排除在外。

泛型

函数泛型

语法为函数名字后面跟一个<参数名> 参数名可以随便写 例如我这儿写了T。

当我们使用这个函数的时候把参数的类型传进去就可以了 (也就是动态类型)

1
2
3
4
5
6
7
8
9
10
11
12
// 函数泛型
function Alive( A : string , B : number ) : Array<string | number>{
return [A , B]
}

// 泛型优化,动态泛型,X,Y为动态类型
function Fn<X,Y>( a : X , b : Y ) : Array<X | Y>{
return [a , b]
}

Alive('我是步君',7)
Fn<string , number>('七海',7)

泛型接口

  • 声明接口的时候 在名字后面加一个<参数>
1
2
3
4
5
6
7
8
9
10
11
interface A<T>{
(arg : T) : T
}

function Fn<T>(args : T) : T {
return args
}

let result : A<string> = Fn

console.log(result('Aliving')) // Aliving

对象字面量泛型

1
2
3
4
5
6
7
let A : { <T>(arg : T) : T }

A = function <T>(arg : T) : T{
return arg
}

console.log(A('天选国V')) // 天选国V

泛型约束

我们期望在一个泛型的变量上面,获取其length参数,但是,有的数据类型是没有length属性的

于是,我们就得对使用的泛型进行约束,我们约束其为具有length属性的类型,这里我们会用到interface

1
2
3
4
5
6
7
8
9
interface Len {
length : number
}

function getLegnth<T extends Len>(arg:T) {
return arg.length
}

console.log(getLegnth<string>('七海小姐姐')) // 5

使用keyof 约束对象

  1. 首先定义了T类型并使用extends关键字继承object类型的子类型,
  2. 然后使用keyof操作符获取T类型的所有键,它的返回类型是联合类型
  3. 最后利用extends关键字约束 K类型必须为keyof T联合类型的子类型
1
2
3
4
5
6
7
8
function prop<T, K extends keyof T>(obj: T, key: K) {
return obj[key]
}

let All = { a: 1, b: 2, c: 3 }

prop(All, 'a')
prop(All, 'd') //此时就会报错发现找不到,

泛型类

声明方法跟函数类似名称后面定义<类型>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Alive<T>{
a : T[] = [];
add(a : T) : T[] {
return [a]
}
}

let x = new Alive<string>()
x.a = ['我','是','Alive','Seven']
console.log(x.add('ABC')) // ['ABC']

let y = new Alive<number>()
y.a = [1,2,3,4]
console.log(y.add(123)) // [ 123 ]