简介 TypeScript是微软 开发的一个开源的编程语言 ,通过在JavaScript 的基础上添加静态类型定义构建而成。TypeScript通过TypeScript编译器 或Babel转译为JavaScript代码,可运行在任何浏览器 ,任何操作系统 。
参考资料
快速入门 简介
为什么要使用 TypeScript? 因为现在主流的前端框架都采用 TS 了,比如我们常见的 Vue React Angular,已然成为了一种趋势
TS是JS的超集,所以JS基础的类型都包含在内
网上流传着一句话 始于 JavaScript,终于 JavaScript 因为浏览器是不认识 TypeScript 所以我们会把 TS 编译成 JS 最终运行的还是 js 文件
起步安装
以下安装均采用 yarn 进行管理,如果是 npm 替换对应的命令即可
安装 @types/node ts-node 是为了在开发环境直接使用命令运行 ts 文件,因为 node 不认识 ts ,如果不安装的话需要先用 tsc xxx.ts 进行编译 然后 node xxx.js 比较麻烦
1 2 3 4 5 6 7 8 9 yarn add typescript -g tsc -v yarn add @types/node --save-dev yarn add ts-node -g ts-node -v
类型约束 string
1 2 3 4 5 6 7 let str : string = "定义一个字符串类型" ;console .log (str);let str2 : string = `引用字符串模板-->${str} ` console .log (str2);
number
number 类型
支持十六进制、十进制、八进制和二进制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 let notANumber : number = NaN ;console .log (notANumber);let num : number = 123 ;console .log (num);let infinityNumber : number = Infinity ;console .log (infinityNumber);let hex : number = 0xf00d ;console .log (hex);let binary : number = 0b1010 ;console .log (binary);let octal : number = 0o744 ;console .log (octal);
boolean
注意,使用构造函数 Boolean
创造的对象不是 布尔值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let objBoolean : Boolean = new Boolean (1 )let falseBoolean : Boolean = new Boolean (false )console .log (objBoolean);console .log (falseBoolean);let boolean : boolean = true let boolean1 : boolean = Boolean (1 )console .log (boolean );console .log (boolean1);
void
JavaScript 没有空值(Void)的概念,在 TypeScript 中,可以用 void
表示没有任何返回值的函数
void
类型的用法,主要是用在我们不希望 调用者关心函数返回值的情况下,比如通常的异步回调函数
void也可以定义undefined 和 null类型
运行前需要先编译成 js 文件用 node 运行(直接用 ts-node 运行 ts 文件会报错)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 let u : void = undefined ;let n : void = null ;console .log (u, n);function voidFn ( ): void { console .log ('test void' ) } function voidFn2 ( ): void { console .log ('test void2' ) return } voidFn ()voidFn2 ()
null_undefined
与 void 的区别是,undefined 和 null 是所有类型的子类型。也就是说 null undefined 类型的变量,可以赋值给 string 类型的变量
void 类型不能赋值给其它类型,编译不会通过
运行前需要先编译成 js 文件用 node 运行(直接用 ts-node 运行 ts 文件会报错)
any
any类型没有强制限定哪种类型,随时切换类型都可以 我们可以对 any 进行任何操作,不需要检查类型
声明变量的时候没有指定任意类型默认为any
弊端如果使用any 就失去了TS类型检测的作用
any 可以是对象,并且调用对应的属性或者方法
1 2 3 4 5 6 7 8 9 10 11 12 let anyStr : any = "string" ;let anyNum : any = 123 ;let anyArray :any =[1 ,4 ,7 ]let anyBoolean :any =true let anySym :any =Symbol ('123' )console .log (anyStr)console .log (anyNum)console .log (anyArray)console .log (anyBoolean)console .log (anySym)
unknown
TypeScript 3.0中引入的 unknown 类型也被认为是 top type ,但它更安全。与 any 一样,所有类型都可以分配给unknown
unknow unknow类型比any更加严格当你要使用any 的时候可以尝试使用unknow
unknow unknown定义的对象不能使用属性或者方法
unknown 类型的只能赋值给 unknown 或者 any 类型的数据
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 let unkStr : unknown = "str" let unkNum : unknown = 123 let unkArray : unknown = [1 , 4 , 7 ]let unkBoolean : unknown = true let unkSym : unknown = Symbol ('123' )console .log (unkStr)console .log (unkNum)console .log (unkArray)console .log (unkBoolean)console .log (unkSym)let unkObj : unknown = {name : "ada" , age : "25" }let unkFun : unknown = { fun : (): number => { return 666 } } let anyObj : any = {name : "ada" , age : "25" }let anyFun : any = { fun : (): number => { return 888 } } console .log (anyObj.age )console .log (anyFun.fun ())
any与 unknown区别
unknown 类定义的对象不能使用对应的属性和方法 any 类型的可以使用对应的属性
unknown可赋值对象只有unknown 和 any
any 类型可赋值给其它任意类型
interface 基础示例
在 typescript 中,我们定义对象的方式要用关键字interface (接口),我的理解是使用interface 来定义一种约束,让数据的结构满足约束的格式
如果有两个 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 interface Person { age : number name : string amount : number } const ada : Person = { age : 25 , name : "ada" , amount : 3000 } interface device { id : string } interface device { name : string } const computer : device = { id : "10010" , name : "Mac" } console .log (computer)console .log (ada.age )
可选式操作符
上面的基本示例中,使用接口约束时必须要求属性和接口一致,如果有些属性希望可有可没有的需要使用可选式操作符 ?
来声明接口中的属性
一般用做在一些新数据和老数据之间,老数据没有该字段
可选属性的含义是该属性可以不存在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 interface Person { age : number , name : string , amount?: number } const ada : Person = { age : 25 , name : "阿达" } const p : Person = { age : 18 , name : "people" , amount : 36000 } console .log (ada)console .log (p)
任意属性proName
当不确定后台返回的属性时,可用 proName 描述任意属性
需要注意的是,一旦定义了任意属性,那么确定属性和可选属性的类型都必须是它的类型的子集
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 interface Person { age : number, name : string, amount?: number, [proName : string]: any } const ada : Person = { age : 25 , name : "阿达" , friends : "萨瓦迪卡" } const p : Person = { age : 18 , name : "people" , amount : 36000 } console .log (ada)console .log (p)
只读属性 readonly
readonly 只读属性,初始化后是不允许被赋值,只能读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 interface Person { age : number , readonly name : string , amount?: number , [proName : string ]: any } const ada : Person = { age : 25 , name : "阿达" , friends : "萨瓦迪卡" } ada.age = 66 console .log (ada)
函数
接口中定义函数
函数定义参数和返回值类型(参数可选,没有返回可用用 void 表示 )
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 interface Person { age : number , readonly name : string , amount?: number , [proName : string ]: any , eat (name : string ): string } const ada : Person = { age : 25 , name : "阿达" , friends : "萨瓦迪卡" , eat (name : string ): string { return `${name} 在吃东西` } } console .log (ada.eat ("阿达" ));
extends
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 interface A { name : string } interface B { age : number } interface P extends A, B { amount : number } let ada : P = { name : "阿达" , age : 88 , amount : 666 } console .log (ada)
Array
常用声明方式
一般可以通过 类型[]
Array<具体类型>
两种方式来进行声明
一般用泛型声明,泛型的类型为 any 是最常用的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 let arrStr : string [] = ['我' , '是' , '阿' , '达' ];let arrNum : number [] = [1 , 3 , 5 , 7 , 9 ];let arrBoolean : boolean [] = [true , false , true ];let arrAny : any [] = ['我' , 1 , true ]let arrStr1 : Array <string > = ['我' , '是' , '阿' , '达1' ];let arrNum1 : Array <number > = [1 , 3 , 5 , 7 , 91 ];let arrBoolean1 : Array <boolean > = [true , false , true , false ];let arrAny1 : Array <any > = ['我' , 1 , true , 1 ];console .log (arrStr)console .log (arrStr1)console .log (arrNum)console .log (arrNum1)console .log (arrBoolean)console .log (arrBoolean1)console .log (arrAny)console .log (arrAny1)
多维数组
数组中的值还是一个数组
和基本定义的一样也有两种方式
arguments
arguments 是入参的集合,该对象需要用 IArguments 类来接收
1 2 3 4 5 6 7 8 9 10 11 12 13 14 function add (...args: any ): void { console .log (arguments ) let arr3 : IArguments = arguments ; console .log (arr3) } add (1 , 2 , 3 )
类数组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 interface ArrNumber { [index : number ]: number } interface ArrString { [index : number ]: string } let arrNumber : ArrNumber = [1 , 2 , 3 , 5 , 7 ]let arrString : ArrString = ['1' , '2' , '3' , '5' , '7' ]console .log (arrNumber)console .log (arrString)
函数扩展 基础使用
默认情况 参数不能多传,也不能少传 必须按照约定的类型来
可通过 ? 号将参数标记为可选参数,例如 amount?: number
在使用该函数的时候被标记的参数就可传可不传了(如果不传的话,再去使用该参数就是 undefined )
也可在声明参数的时候给默认值,例如 amount: number = 0
如果不传该参数就会使用默认值,传了就会以实际传参为准
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 const fun = (age : number , name : string , amount : number ): string => `age=${age} ,name=${name} ,amount=${amount} ` ;let ada = fun (18 , 'ada' , 10000 )console .log (ada)const fun1 = (age : number , name : string , amount?: number ): string => `age=${age} ,name=${name} ,amount=${amount} ` ;let ada1 = fun1 (18 , 'ada' )console .log (ada1)const fun2 = (age : number , name : string , amount : number = 0 ): string => `age=${age} ,name=${name} ,amount=${amount} ` ;let ada2 = fun2 (18 , 'ada' )let ada3 = fun2 (18 , 'ada' , 3000 )console .log (ada2)console .log (ada3)
接口约束函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 interface User { name : string , age : number , amount?: number } const printUser = (user : User ): User => { return user } console .log (printUser ( { name : "阿达" , age : 19 , } ) )
函数的重载
函数名相同,参数列表不同
参数列表包括参数的类型和个数以及参数的顺序
与返回类型无关
如果参数类型不同,则参数类型应设置为 any 。
参数数量不同你可以将不同的参数设置为可选
1 2 3 4 5 6 7 8 9 10 function fun (params: string ): string ;function fun (params: string , params2: number ): any ;function fun (params: string , params2?: number ): string { return params + params2 } console .log (fun ('1' ));console .log (fun ('我' , 123 ));
剩余参数(展开)
可变参数用 …args 来表示
在定义可变参数时,也可以定义一些固定参数,然后用可变参数定义剩余参数
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' )
联合类型
联合类型,能约束属性为多个类型中的一个
比如工作中需求瞬息万变,一个联系人的电话既可以存 11 位手机号,又可以存座机号或者其它场景要求 既可以传参 0或1,又可以传参布尔值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 let phone : number | string = '021-09xxxx98' let phone2 : number | string = 13566778899 console .log (phone.length )console .log (phone2)let fn = (beautiful : number | boolean ): boolean => { return !!beautiful; } console .log (fn (0 ));console .log (fn (true ));
交叉类型
多种类型的集合,联合对象将具有所联合类型的所有成员
如下示例 info 参数为 Person Man 的交叉类型,传递参数时需要传递两个类型的所有必填参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 interface Person { name : string age : number } interface Man { hasBeard : boolean } let ada = (info : Person & Man ): void => { console .log (info) } ada ({name : "阿达" , age : 18 , hasBeard : true })
类型断言
需要注意的是,类型断言只能够「欺骗」TypeScript 编译器,无法避免运行时的错误,反而滥用类型断言可能会导致运行时错误
语法: 值 as 类型 value as string
或 <类型>值 <string>value
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 let fun = (amount : number | string ): void => { console .log ((amount as string ).length ) } fun ("10元" )fun (10 )interface F1 { run : string } interface F2 { build : string } let fun2 = (F : F1 | F2 ): void => { console .log ((<F2 >F).build ) } fun2 ({build : "bui" })fun2 ({run : "bui" })
1 2 3 4 5 window .abc = 123 (window as any ).abc = 123
断言型断言是不具影响力的
在下面的例子中,将 something 断言为 boolean 虽然可以通过编译,但是并没有什么用 并不会影响结果, 因为编译过程中会删除类型断言
1 2 3 4 5 6 function toBoolean (something: any ): boolean { return something as boolean ; } toBoolean (1 );
内置对象 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 ('哔哩哔哩XXXX' )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)
DOM 内置对象
**Document
、HTMLElement
、NodeList
**
1 2 3 4 5 6 7 let body : HTMLElement = document .body ;let allDiv : NodeList = document .querySelectorAll ('div' );let div : HTMLElement = document .querySelector ('div' ) as HTMLDivElement document .addEventListener ('click' , function (e: MouseEvent ) {});
BOM 内置对象
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 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 ; }
Promise
定义 Promise 时,如果我们不指定返回的类型TS是推断不出来返回的是什么类型
1 2 3 4 5 6 7 8 9 10 function promise ( ): Promise <number > { return new Promise <number >((resolve, reject ) => { resolve (1 ) }) } promise ().then (res => { console .log (res) })
Class 类的基本声明
在TypeScript是不允许直接在constructor 定义变量的 需要在constructor上面先声明
如果了定义了属性但是构造函数不进行初始化 也会报错 通常是给个默认值(在定义属性的时候给个默认值) 或者 进行赋值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 class Person { name : string age : number amount : number | string gender : string = "未知" constructor (name: string , age: number , amount: number | string ) { this .name = name; this .age = age; this .amount = amount; } } let ada = new Person ("ada" , 18 , "10元" );let ada2 = new Person ("阿达" , 20 , 100 );console .log (ada, ada2)
修饰符
TS 中类属性有三个权限修饰符 public private protected
属性显示声明的情况下默认就是 public
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 class Person { public name : string protected age : number private amount : number | string gender : string = "未知" constructor (name: string , age: number , amount: number | string ) { this .name = name; this .age = age; this .amount = amount; } } let ada = new Person ("ada" , 18 , "10元" );console .log (ada.name , ada.gender )class Man extends Person { constructor (name: string , age: number , amount: number | string ) { super (name, age, amount); console .log (this .age , this .name , this .gender ); } } new Man ("TypeScript" , 5 , 300 )
static 修饰符
静态方法只能访问静态属性(和 java 差不多)
类中的静态函数之间可以用 this 相互调用
非静态函数中如果要调用静态函数需要用 类名.静态函数名
外部调用静态函数也是用 类名.静态函数名 不需要再单独 New 对象了
static 属性在构造器中也只能用 类名.静态属性名
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 class Person { public name : string protected age : number = 23 private static amount : number | string = 0 constructor (name: string , age: number ) { this .name = name; this .age = age; console .log ("初始化对象" ) console .log (Person .getPerson ()); } static getAmount ( ) { return this .amount } static getPerson ( ) { return this .getAmount () } } console .log (Person .getAmount ());new Person ("ada" , 20 )
interface
interface 定义类 使用关键字 implements 后面跟interface的名字多个用逗号隔开 继承还是用extends
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 interface Person { get (number : number ): string } interface Man { say (): string } class Woman { eat (): boolean { return true } } class User extends Woman implements Person , Man { get (number : number ): string { if (number === 1 ) { return "---子类实现接口未显示的方法" ; } else { return "+++子类实现接口未显示的方法" ; } }; say (): string { return "Hello, world!" ; } } let user = new User ;console .log (user.get (5 ));console .log (user.say ());console .log (user.eat ());
abstract
应用场景如果你写的类实例化之后毫无用处此时我可以把他定义为抽象类
或者你也可以把他作为一个基类-> 通过继承一个派生类去实现基类的一些方法
抽象类中可以有普通函数和抽象函数
普通函数可以实现具体方法,派生类不需要实现该方法
抽象函数不能实现具体业务,派生类必须要实现该方法
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 abstract class Abs { name : string protected constructor (name: string ) { this .name = name; } setName (name : string ): void { this .name = name } abstract getName (): string } class PersonAbs extends Abs { constructor ( ) { super ("ada" ); } getName (): string { return this .name ; } } let p = new PersonAbs ()console .log (p.getName ());p.setName ("setName" ) console .log (p.getName ());
元组Tuple
元组就是数组的变种
元组 Tuple 是固定数量的不同类型的元素的组合(但是也可以越界 push)
元组与集合的不同之处在于,元组中的元素类型可以是不同的,而且数量固定。元组的好处在于可以把多个元素作为一个单元传递。如果一个方法需要返回多个值,可以把这多个值作为元组返回,而不需要创建额外的类来表示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 let arrTuple : [string , number ] = ["1" , 1 ]let arrTuple2 : [string , number , boolean ] = ["1" , 1 , false ]console .log (arrTuple[0 ].length );console .log (arrTuple)arrTuple.push (2 , 'ada' ) console .log (arrTuple)let excel : [string , string , number , string ][] = [ ['title1' , 'name1' , 1 , '123' ], ['title2' , 'name2' , 2 , '123' ], ['title3' , 'name3' , 3 , '123' ], ['title4' , 'name4' , 4 , '123' ], ['title5' , 'name5' , 5 , '123' ], ]
enum
在 JavaScript 中是没有枚举的概念的 TS 帮我们定义了枚举这个类型
数字枚举 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 enum Color { RED , GREEN , BLUE , } console .log (Color .RED , Color .GREEN , Color .BLUE )enum Color2 { RED = 3 , GREEN , BLUE , } console .log (Color2 .RED , Color2 .GREEN , Color2 .BLUE )enum Color3 { RED = 1 , GREEN = 5 , BLUE , } console .log (Color3 .RED , Color3 .GREEN , Color3 .BLUE )
字符串枚举
字符串枚举的概念很简单。 在一个字符串枚举里,每个成员都必须用字符串字面量,或另外一个字符串枚举成员进行初始化。
由于字符串枚举没有自增长的行为,字符串枚举可以很好的序列化。 换句话说,如果你正在调试并且必须要读一个数字枚举的运行时的值,这个值通常是很难读的 - 它并不能表达有用的信息,字符串枚举允许你提供一个运行时有意义的并且可读的值,独立于枚举成员的名字
下面的 Color1 其实算是一个异构枚举(这里只是为了对比)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 enum Color { RED = "red" , GREEN = "green" , BLUE = "blue" , } console .log (Color .RED , Color .GREEN , Color .BLUE )enum Color1 { RED , GREEN = "green" , BLUE = "blue" , } console .log (Color1 .RED , Color1 .GREEN , Color1 .BLUE )
异构枚举
1 2 3 4 5 6 7 8 9 enum bool { YES = 'true' , NO = 0 , UNKNOWN } console .log (bool.YES , bool.NO , bool.UNKNOWN )
接口枚举 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 enum Color { RED = 1 , GREEN , BLUE , } interface Col { red : Color .RED } let C : Col = { red : 4 } let C2 : Col = { red : Color .RED } console .log (C.red )console .log (C2 .red )
const枚举
什么枚举 let 和 var 都是不允许的声明只能使用const 或者不用
大多数情况下,枚举是十分有效的方案。 然而在某些情况下需求很严格。 为了避免在额外生成的代码上的开销和额外的非直接的对枚举成员的访问,我们可以使用 const
枚举。 常量枚举通过在枚举上使用 const
修饰符来定义
const 声明的枚举会被编译成常量,普通声明的枚举编译完后是个对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const enum bool { YES = 'true' , NO = 0 , } if (bool.NO === 0 ) {} enum bool2 { YES = 'true' , NO = 0 , } if (bool2.NO === 0 ) {}
反向映射
它包含了正向映射( name
-> value
)和反向映射( value
-> name
)
要注意的是 不会 为字符串枚举成员生成反向映射
1 2 3 4 5 6 7 8 9 10 11 12 13 14 enum Type { SUCCESS = 'true' , FAIL = 0 } let SUCCESS = Type .SUCCESS let failureValue = Type .FAIL console .log (SUCCESS , failureValue)let failKeys = Type [failureValue]console .log (failKeys)
类型推论&类型别名
什么是类型推论?—> 我声明了一个变量,在没有约束类型的情况对其进行了赋值,那么 TS 就会根据赋值的类型推断出实际的类型
如果你声明变量没有定义类型也没有赋值这时候 TS 会推断成 any 类型可以进行任何操作
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 let a = 'str' let b = 123 let anan = "123" an = 123 an = false an = [1 , "22" , false ] console .log (a, b, an)type s = string let username : s = "ada" type snb = string | number | boolean let amount : snb = 0 amount = "0元" amount = false console .log (username, amount)type str = () => string const getName : str = () => "ada函数式别名" console .log (getName ());type status = "on" | "off" | false let s : status = "on" console .log (s)
never 类型
TypeScript 将使用 never 类型来表示不应该存在的状态
never 与 void 差异: ① void 类型只是没有返回值但本身不会出错 ② never 只会抛出异常没有返回值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 let never1 : string & number function error (message: string ): never { throw new Error (message); } function loop ( ): never { while (true ) { } }
never 类型的一个应用场景:现在有 Working Happy 两个接口,产品加了一个新需求,刚好老同事离职了,让新同事加个 Money 接口,我们必须手动找到所有 switch 代码并处理,否则将有可能引入 BUG ,而且这将是一个“隐蔽型”的BUG,如果回归面不够广,很难发现此类BUG,那 TS 有没有办法帮助我们在类型检查阶段发现这个问题呢?当然是有的,由于任何类型都不能赋值给 never
类型的变量,所以当存在进入 default
分支的可能性时,TS的类型检查会及时帮我们发现这个问题
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 interface Working { today : "工作日" } interface Happy { today : "休息日" } interface Money { today : "今天 10 号发工资" } type days = Working | Happy | Money function f (day: days ) { switch (day.today ) { case "工作日" : console .log ("搬砖" ) break case "休息日" : console .log ("看电影" ) break default : const check : never = day break } }
Symbol
Symbol 是ES6 新增的一个类型,意寓着象征的意思
symbol
类型的值是通过Symbol
构造函数创建的。
可以传递参做为唯一标识 只支持 string 和 number类型的参数,其它类型的值会被 toString
基础使用 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 str = Symbol ('字符串' )let num = Symbol (123 )console .log (str, num)let obj = { [str]: "stri" , [num]: 18 , name : "ada" , arg : "25" } for (let key in obj) { console .log (key) } console .log (Object .keys (obj))console .log (Object .getOwnPropertyNames (obj))console .log (JSON .stringify (obj));console .log (Object .getOwnPropertySymbols (obj))console .log (Reflect .ownKeys (obj))
Iterator迭代器
迭代器不支持对象(对象没有Symbol.iterator() 方法) Map Set Array 这些都支持
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 let arr : Array <number > = [1 , 3 , 5 ]let numbers : Iterator <number > = arr[Symbol .iterator ]();console .log (numbers.next ());console .log (numbers.next ());console .log (numbers.next ());console .log (numbers.next ());let arr2 : Array <number > = [1 , 3 , 5 ]let set : Set <string > = new Set <string >(["我" , "是" , "ada" ]);let map : Map <string , any > = new Map <string , any >()map.set ("name" , "ada" ) map.set ("age" , 20 ) let iterator = (args : any ): void => { let it : Iterator <any > = args[Symbol .iterator ]() let next : any = {done : false } while (!next.done ) { next = it.next () if (!next.done ) { console .log (next.value ); } } } iterator (arr2)iterator (set)iterator (map)
for of
for of 就像是 iterator 迭代器的语法糖,底层会自动调用Symbol.iterator() 方法帮我们进行迭代
同样也不支持对象
与 for in 的区别: for in 主要针对与 Array 迭代出来的是索引,for of 支持遍历大部分类型迭代器 arr nodeList argumetns set map 等,迭代出来的是 value
ps:截图中我的 let item of items 编辑器在报错,但是也能正常运行不知道咋回事
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 let arr : Array <number > = [1 , 3 , 5 ]let set : Set <string > = new Set <string >(["我" , "是" , "ada" ]);let map : Map <string , any > = new Map ()map.set ("name" , "ada" ) map.set ("age" , 20 ) type asm = Array <number > | Set <string > | Map <string , any >let forOf = (items : asm): void => { for (let item of items) { console .log (item) } console .log ("------------------------" ) } forOf (arr)forOf (set)forOf (map)
其它函数
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作用域排除在外。
泛型
泛型在 TS 中是很重要的东西,例如 VUE3 是用 TS 编写的,里面用得到了非常多的泛型
泛型函数
在我们的场景中会有很多功能一样的函数,但是需要的参数类型却是各不相同,如下两端代码,功能一致,但是对传参类型一个是 number,另外一个是 string
1 2 3 4 5 6 7 8 9 10 11 function num (a: number , b: number ): Array <number > { return [a, b]; } num (15 , 20 )function str (a: string , b: string ): Array <string > { return [a, b]; } str ("好好学" , "上网" )
泛型函数语法为函数名字后面跟一个<参数名> 参数名可以随便写 例如我这儿写了 T
当我们使用这个函数的时候把参数的类型传进去就可以了(也就是动态类型)
我们也可以使用不同的泛型参数名,只要在数量上和使用方式上能对应上就可以
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 function returnInput<T>(a : T, b : T): Array <T> { return [a, b] } console .log (returnInput<number >(15 , 20 ));console .log (returnInput (15 , 20 ));console .log (returnInput ("好好学" , "上网" ));function returnInput2<N, S>(a : N, b : S): Array <N | S> { return [a, b] } console .log (returnInput2 (5 , "幼儿园" ));
泛型约束
上面我们已经实现了泛型化了,但是这种泛型太任意了,可能会导致传递的参数类型与我们预期的不一致,因此我们可以对泛型进行一定的约束 使用 extends 关键字
1 2 3 4 5 6 7 8 9 10 11 12 13 interface len { length : number } function f<T extends len>(a : T): number { return a.length } console .log (f ("123" ));console .log (f ([1 , 2 , 4 , "China" ]));console .log (f ({length : 10 , age : 19 }));
keyof
其中使用了TS泛型和泛型约束。首先定义了T类型并使用extends关键字继承object类型的子类型,然后使用keyof操作符获取T类型的所有键 ,它的返回 类型是联合 类型,最后利用extends关键字约束 K类型必须为keyof T联合类型的子类型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 function prop<O, K extends keyof O>(obj : O, key : K) { return obj[key]; } enum Gender { WOMAN = 0 , MAN = 1 , OTHER = 2 } let user = { name : "阿达" , age : 18 , grand : Gender .MAN } console .log (prop (user, "name" ));console .log (prop (user, "age" ));console .log (prop ("abc" , "length" ));
泛型类
声明方法跟函数类似名称后面定义<类型>
使用的时候确定类型new Sub<类型>()
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 Clazz <S, N> { str : S num : N constructor (str: S, num: N ) { this .str = str this .num = num } arr (a : S, b : N): Array <S | N> { return [a, b] } getStr (a : S): S { return a } getNum (b : N, c : N): N[] { return [b, c] } } let cla = new Clazz <string , number >("string" , 0 )cla.str = "string 类型" cla.num = 10 console .log (cla.arr ("1" , 2 ));console .log (cla.getStr ("参数只能传 S 类型的" ));console .log (cla.getNum (1 , 2 ));