TypeScript类型声明书写 
本文总结一下TypeScript类型声明的书写,很多时候写TypeScript不是问题,写类型就特别纠结,我总结下,我在使用TypeScript中遇到的问题。如果你遇到类型声明不会写的时候,多看看lodash的声明,因为lodash对数据进行各种变形操作,所以你能遇到的,都有参考示例。
 
基本类型 1 2 3 4 5 6 7 8 9 10 const  num: number  = 1 ;const  str: string  = 'str' ;const  bool: boolean  = true ;const  nulls: null  = null ;const  undefine: undefined  = undefined ;const  symbols: Symbol = Symbol('symbal' );const  any : any  = 'any types' ; 
 
数组 1 2 3 4 5 6 7 const  nums: number [] = [1 , 2 , 3 , 4 ];const  strs: Array <string > = ['s' , 't' , 'r' ];const  dates: Date [] = [new  Date (), new  Date ()];
 
数组的concat方法,返回类型为never[]问题 1 2 3 4 5 6 7 const  arrNever: string [] = [].concat(['s' ]);const  fixArrNever: string [] = ([] as  string []).concat(['s' ]);
 
接口 接口是 TypeScript 的一个核心知识,它能合并众多类型声明至一个类型声明:
而且接口可以用来声明:函数,类,对象等数据类型
1 2 3 4 5 6 7 8 9 interface  Name {  first: string ;   second: string ; } let  username: Name = {  first: 'John' ,   second: 'Doe'  }; 
 
any、null、undefined、void类型 1 2 3 4 5 6 7 8 9 10 11 12 const  any : any  = 'any types' ; let  nobody: any  = 'nobody, but you' ;nobody = 123 ; let  nulls: number  = null ;let  bool: boolean  = undefined ;function  printUsername  (name: string  ): void   {    console .log(name); } 
 
联合类型 
联合类型在option bags模式场景非常实用,使用  |  来做标记
 
1 2 3 4 5 6 function  options (opts: {     types?: string ;     tag: string  | number ; } ): void   {     } 
 
交叉类型 
最典型的使用场景就是继承 和mixin ,或者copy等操作
 
1 2 3 4 function  $extend <T , U >(first: T, second: U ): T  & U   {  return  Object .assign(first, second);  } 
 
元组 tuple 
元组很少使用
 
1 2 3 4 5 6 7 8 9 10 11 12 let  nameNumber: [string , number ];nameNumber = ['Jenny' , 221345 ]; let  tuple: [string , number ];nameNumber = ['Jenny' , 322134 ]; const  [usernameStr, uselessNum] = nameNumber;
 
type的作用 
type用来创建新的类型,也可以重命名(别名)已有的类型,建议使用type创建简单类型,无嵌套的或者一层嵌套的类型,其它复杂的类型都应该使用interface, 结合implements ,extends实现。
 
1 2 3 4 5 6 7 8 9 type  StrOrNum = string  | number ;let  sample: StrOrNum;sample = 123 ; sample = '123' ; sample = true ;  
 
实践中遇到的问题 第三方库没有提供声明d.ts文件 
如果第三方库没有提供声明文件,第一时间去微软官方的仓库https://github.com/borisyankov/DefinitelyTyped    查找,或者在npmjs.com上搜索@types/依赖的模块名大部分情况都可以找到。
 
手动添加声明文件
声明文件一般都是统一放置在types文件夹下
 
1 2 declare  module  'axios'; // 这里的axios声明为any类型
 
 
 
全局变量 
例如一些库直接把在window上添加的全局变量
 
1 2 3 4 declare  const  jQuery: any ;declare  const  $: typeof  jQuery;
 
非JavaScript资源 
在前端工程中,import很多非js资源,例如:css, html, 图片,vue, 这种ts无法识别的资源时,就需要告诉ts,怎么识别这些导入的资源的类型。
 
1 2 3 4 5 6 7 8 9 10 declare  module  '*.vue' {  import  Vue from  'vue' ;   export  default  Vue; } declare  module  '*.html';// css declare module  '*.css'; 
 
强制类型转换 
有时候遇到需要强制类型转换,尤其是对联合类型或者可选属性时。
 
1 2 3 4 5 const  convertArrType: string [] = <Array <string >>[].concat(['s' ]);const  fixArrNever: string [] = ([] as  string []).concat(['s' ]);
 
建议使用第二种,因为兼容JSX,第一种官方也不推荐了,虽然它是合法的。
 
可选属性和默认属性 
API中提供的参数很多都有默认值,或者属性可选,怎么书写呢?
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 class  Socket {}export  type  SocketType = 'WebSocket'  | 'SockJs' ;export  interface  SocketOptions {  type : SocketType;   protocols?: string  | string [];    pingMessage: string  | (( ) => string  ); // 联合类型,可以为string 或者函数    pongMessage : string  | (( ) => string  ); } // 默认值 export  function  eventHandler  = (  evt: CloseEvent | MessageEvent | Event,   socket: Socket,   type  = 'WebSocket'   ) =>  any ;
 
独立函数怎么声明类型 
刚开始我也很纠结这个问题,我就是一个独立的函数,怎么声明类型呢?尤其是写事件处理函数 的API时。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 class  Socket {}export  type  SocketEventHandler = (  evt: CloseEvent | MessageEvent | Event,   socket: Socket ) => any ; const  eventHandler: SocketEventHandler = (evt, socket ) =>  {} let  baz = (x = 1 ) =>  {};let  foo = (x: number , y: number  ) =>  {};let  bar = (x?: number , y?: number  ) =>  {};let  bas = (...args: number [] ) =>  {};
 
索引属性类型声明 
JavaScript中的对象都可以使用字符串索引直接取属性或者调用方法,TypeScript中也有相应的类型声明方法。
 
1 2 3 4 5 6 7 8 9 10 11 type  Hello = {    hello: 'world' ;          [key: string ]: string ; }; const  greeting: Hello = {    hi: 'morning'  } console .log(greeting['hi' ])
 
动态添加的属性声明 
有的时候我们只声明了一个基本的类型结构,然后后续有扩展的情况 ,尤其时二次封装时的options。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 interface  AxiosOptions {}type  AjaxOptions = {   axiosOptions: AxiosOptions;        extraOptions: {        [prop: string ]: any     };  }; type  AjaxOptions1 = {  axiosOptions?: AxiosOptions;         [prop: string ]: any  }; const  ajaxOptions: AjaxOptions1 = {  axiosOptions1: {};  } 
 
!的使用 
! 标识符告诉ts编译器,声明的变量没有问题,再运行期不会报错。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class  BaseSelect extends  Vue {    options: string [];           created() {         this .options = ['inited' ]     } } class  BaseSelect extends  Vue {    options!: string [];           created() {         this .options = ['inited' ]     } } 
 
this的使用 
对于独立使用的函数,可以声明指定的调用上下文
 
1 2 3 4 5 6 7 8 9 10 class  Handler {    info: string ;          onClickBad(this : Handler, e: Event) {                  this .info = e.message;     } } let  h = new  Handler();uiElement.addClickListener(h.onClickBad);  
 
声明合并(扩展Vue声明) 
来看看使用场景,扩展vue,在vue上添加全局的属性。
 
1 2 3 4 5 6 7 8 9 10 11 12 13 declare  module  'vue/types/vue' {     interface  Vue {       $eventBus: Vue;   }           interface  VueConstructor {     $eventBus: Vue;   } } 
 
总结 
TypeScript声明还有很多高级的用法,目前我也没有用到那么多,在我纠结不会写声明的时候,我就会看看别人的声明文件时怎么写的。
 
注意:尽量不要把解构 和声明 写在一起,可读性极差。 
1 2 3 4 5 6 7 8 9 10 class  Node {  onNodeCheck(checkedKeys: any , {        checked, checkedNodes, node, event,     } : {        node: any ;       [key: string ]: any ;     }   ) {    } }