前言: 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、null
、undefined
以及 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 ;let num : number = 1234 ;let infinityNumber : number = Infinity ;let decimal : number = 8 ;let hex : number = 0xf00d ;let binary : number = 0b1010 ;let octal : number = 0o744 ;
布尔类型 1 2 3 4 5 6 7 8 9 10 let b : boolean = false let b2 : boolean = true let b3 : boolean = Boolean (1 )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)
1 2 3 4 5 6 function fn ( ) : void { console .log (123 ) } fn ()
void
类型的用法,主要是用在我们不希望 调用者关心函数返回值的情况下,比如通常的异步回调函数
void和undefined和null的区别
1 2 3 4 5 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
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 let value : unknown ; value = true ; value = 42 ; value = "Hello World" ; value = []; value = {}; value = null ; value = undefined ; value = Symbol ("type" ); let a :unknown = '123' let b :string = a let a :any = '123' let b :string = a let bbb :unknown = '123' let aaa :any = '456' aaa = bbb
接口和对象类型 接口
在ts中,我们定义对象的方式要用interface (接口)。
接口的作用:在面向对象的编程中,接口是一种规范的定义,它定义了行为和动作的规范,在程序设计里面,接口起到一种限制和规范 的作用。
1 2 3 4 5 6 7 8 9 10 11 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 A { name : string } interface A { age : number } let obj : A = { name : 'AliveSeven' , age : 20 } console .log (obj)
继承
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)
可选属性:?操作符
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 interface A { [propName : string ] : any name : string age? : number } let a : A = { name : 'AliveSeven' , sex : 'boy' } console .log (a)
只读属性 readonly
readonly 只读属性是不允许被赋值的只能读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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)
数组类型
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 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) console .log (z)
用接口表示数组 1 2 3 4 5 6 7 interface A { [index : number ] : number } let a : A = [1 ,2 ,3 ,4 ] console .log (a)
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 ) let arr :number [] = arguments } Arr (111 , 222 , 333 )function Arr (...args:any ): void { console .log (arguments ) let arr :IArguments = arguments } Arr (111 , 222 , 333 ) interface IArguments { [index : number ]: any ; length : number ; callee : Function ; }
函数扩展 函数类型
fn = (函数变量1 , 函数变量2 …) : 函数类型 => { 函数返回值 }
1 2 3 4 5 6 7 8 const fn = (name : string , age : number , sex? : string ) : string => { return (name + age + sex) } let a : string = fn ('AliveSeven' ,20 )console .log (a)
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 )
函数重载
重载是方法名字相同 ,而参数不同 ,返回类型可以相同也可以不同。
参数类型不同时,应设置为 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 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 interface A { run : string } interface B { build : string } const fn = (type : A | B): string => { return (type as A).run }
使用any临时断言 1 2 (window as any ).abc = 123
as 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 ); 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 name : string private age : number protected sex : any 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 )console .log (AliveSeven .age )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 name : string private age : number protected sex : any static GPA : number = 3.5 constructor (name : string , age : number , sex : any ) { this .name = name this .age = age this .sex = sex } static run ( ){ return this .A () } static A ( ){ return 'I am Aliving' } } class Man extends Person { constructor ( ) { super ('Alive7' ,22 ,'male' ) 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' } } class D extends C implements A,B{ AI : string constructor ( ){ super () this .AI = 'Aliving' } get (type : boolean ){ return type } 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 ());
元组类型
元组(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 ]console .log (typeof (arr[0 ])) console .log (arr[1 ].length )
枚举类型 数字枚举 例如:红绿蓝 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 enum Types { Red = 0 , Green = 1 , BLue = 2 }
增长枚举 1 2 3 4 5 enum Types { Red = 1 , Green , BLue }
如上,我们定义了一个数字枚举, Red使用初始化为 1
。 其余的成员会从 1
开始自动增长。 换句话说, Type.Red
的值为 1
, Green
为 2
, Blue
为 3
。
字符串枚举
字符串枚举的概念很简单。 在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化
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 } console .log (Types .A ) console .log (Types [1 ])
类型推论 | 类型别名 类型推论 我声明了一个变量但是没有定义类型。
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);
定义联合类型 别名
1 2 3 4 5 6 7 type str = string | number let s : str = 123 let s2 : str = '123' console .log (s,s2);
定义值 的别名
1 2 3 4 type value = boolean | 0 | '123' let A :value = true
never类型 TypeScript 将使用 never 类型来表示不应该存在的状态
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 type A = number & string function loop ( ) : never { while (true ){ } } function error (message: string ): never { throw new Error (message); }
never 与 void 的差异 1 2 3 4 5 6 7 8 9 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 | Bfunction 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
成为了一种新的原生类型,就像number
和string
一样。
symbol
类型的值是通过Symbol
构造函数创建的。
1 2 3 4 5 let sym1 = Symbol ();let sym2 = Symbol ("key" ); console .log (sym2)
Symbols是不可改变且唯一的。 1 2 3 4 let sym2 = Symbol ("key" );let sym3 = Symbol ("key" );sym2 === sym3;
用做对象属性的键 1 2 3 4 5 6 7 let sym = Symbol ();let obj = { [sym]: "value" }; console .log (obj[sym]);
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' } for (const key in obj) { console .log (key) } Object .keys (obj)console .log (Object .keys (obj))console .log (Object .getOwnPropertyNames (obj))console .log (JSON .stringify (obj))
遍历结果:
1 2 3 4 5 age sex [ 'age', 'sex' ] [ 'age', 'sex' ] {"age":21,"sex":"male"}
如何拿到:
Object.getOwnPropertySymbols方法
es6 的 Reflect 拿到对象的所有属性
1 2 3 4 5 6 Object .getOwnPropertySymbols (obj)console .log (Object .getOwnPropertySymbols (obj))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 ()); console .log (iterator.next ()); console .log (iterator.next ()); console .log (iterator.next ()); console .log (iterator.next ());
案例:
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 } 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)
以下为这些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] } 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' ))
对象字面量泛型 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' ))
泛型约束 我们期望在一个泛型的变量上面,获取其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 >('七海小姐姐' ))
使用keyof 约束对象
首先定义了T类型并使用extends关键字继承object类型的子类型,
然后使用keyof操作符获取T类型的所有键,它的返回类型是联合类型
最后利用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' )) let y = new Alive <number >()y.a = [1 ,2 ,3 ,4 ] console .log (y.add (123 ))