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 ; } ) { } }