TypeScript类型声明书写详解
本文总结一下typescript类型声明的书写,很多时候写typescript不是问题,写类型就特别纠结,我总结下,我在使用typescript中遇到的问题。如果你遇到类型声明不会写的时候,多看看lodash的声明,因为lodash对数据进行各种变形操作,所以你能遇到的,都有参考示例。
基本类型
// 变量 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'; // typescript的any类型,相当于什么类型约束都没有
数组
// 数组: 推荐使用t[]这种写法 const nums: number[] = [1, 2, 3, 4]; // 不推荐:array<t>泛型写法,因为在jsx中不兼容,所以为了统一都使用t[]这种类型 const strs: array<string> = ['s', 't', 'r']; const dates: date[] = [new date(), new date()];
数组的concat方法,返回类型为never[]问题
// 数组concat方法的never问题 // 提示: type 'string' is not assignable to type 'never'. const arrnever: string[] = [].concat(['s']); // 主要问题是:[]数组,ts无法根据上下文判断数组内部元素的类型 // @see https://github.com/microsoft/typescript/issues/10479 const fixarrnever: string[] = ([] as string[]).concat(['s']);
接口
接口是 typescript 的一个核心知识,它能合并众多类型声明至一个类型声明:
而且接口可以用来声明:函数,类,对象等数据类型
interface name { first: string; second: string; } let username: name = { first: 'john', second: 'doe' };
any、null、undefined、void类型
// 特殊类型 const any: any = 'any types'; // typescript的any类型,相当于什么类型都没写 let nobody: any = 'nobody, but you'; nobody = 123; let nulls: number = null; let bool: boolean = undefined; // void function printusername (name: string): void { console.log(name); }
联合类型
联合类型在option bags模式场景非常实用,使用 **| **来做标记
function options(opts: { types?: string; tag: string | number; }): void { }
交叉类型
最典型的使用场景就是继承和mixin,或者copy等操作
// 交叉类型:如果以后遇到此种类型声明不会写,直接看object.assign声明写法 function $extend<t, u>(first: t, second: u): t & u { return object.assign(first, second); // 示意而已 }
元组 tuple
元组很少使用
let namenumber: [string, number]; // ok namenumber = ['jenny', 221345]; // error // namenumber = ['jenny', '221345']; let tuple: [string, number]; namenumber = ['jenny', 322134]; const [usernamestr, uselessnum] = namenumber;
type的作用
ype用来创建新的类型,也可以重命名(别名)已有的类型,建议使用type创建简单类型,无嵌套的或者一层嵌套的类型,其它复杂的类型都应该使用interface, 结合implements ,extends实现。
type strornum = string | number; // 使用 let sample: strornum; sample = 123; sample = '123'; // 会检查类型 sample = true; // error
实践中遇到的问题
第三方库没有提供声明d.ts文件
如果第三方库没有提供声明文件,第一时间去微软官方的仓库https://github.com/borisyankov/definitelytyped 查找,或者在npmjs.com上搜索@types/依赖的模块名大部分情况都可以找到。
手动添加声明文件
声明文件一般都是统一放置在types文件夹下
// 例子: types/axios.d.ts declare module 'axios'; // 这里的axios声明为any类型
全局变量
例如一些库直接把在window上添加的全局变量
// globals.d.ts // 例子:jquery,现实中jquery是有.d.ts declare const jquery: any; declare const $: typeof jquery;
非javascript资源
在前端工程中,import很多非js资源,例如:css, html, 图片,vue, 这种ts无法识别的资源时,就需要告诉ts,怎么识别这些导入的资源的类型。
// 看看vue怎么处理的:shims-vue.d.ts declare module '*.vue' { import vue from 'vue'; export default vue; } // html declare module '*.html'; // css declare module '*.css';
强制类型转换
有时候遇到需要强制类型转换,尤其是对联合类型或者可选属性时。
// 第一种:使用<>括号 const convertarrtype: string[] = <array<string>>[].concat(['s']); // 第二种:使用as关键字 const fixarrnever: string[] = ([] as string[]).concat(['s']);
建议使用第二种,因为兼容jsx,第一种官方也不推荐了,虽然它是合法的。
可选属性和默认属性
api中提供的参数很多都有默认值,或者属性可选,怎么书写呢?
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时。
class socket {} // 函数的声明方式 export type socketeventhandler = ( evt: closeevent | messageevent | event, socket: socket ) => any; const eventhandler: socketeventhandler = (evt, socket) => { } // 可选参数和rest参数 let baz = (x = 1) => {}; let foo = (x: number, y: number) => {}; let bar = (x?: number, y?: number) => {}; let bas = (...args: number[]) => {};
索引属性类型声明
javascript中的对象都可以使用字符串索引直接取属性或者调用方法,typescript中也有相应的类型声明方法。
type hello = { hello: 'world'; // key只是一个形式属性名(类似形参一样) [key: string]: string; }; const greeting: hello = { hi: 'morning' } console.log(greeting['hi'])
动态添加的属性声明
有的时候我们只声明了一个基本的类型结构,然后后续有扩展的情况 ,尤其时二次封装时的options。
interface axiosoptions {} type ajaxoptions = { axiosoptions: axiosoptions; // 额外扩展的放入到单独的属性节点下 extraoptions: { [prop: string]: any }; }; type ajaxoptions1 = { axiosoptions?: axiosoptions; // 不要这样写,因为axiosoptions拼写错误时,ts不会提示 // 尽量把后续扩展的属性,移动到独立的属性节点下 [prop: string]: any }; const ajaxoptions: ajaxoptions1 = { axiosoptions1: {}; // 本意是axiosoptions,但是ts不会提示 }
!的使用
! 标识符告诉ts编译器,声明的变量没有问题,再运行期不会报错。
class baseselect extends vue { options: string[]; // 这里会提示没有在constructor中初始化 created() { this.options = ['inited'] } } class baseselect extends vue { options!: string[]; // 使用 ! 告诉编译器,我知道自己在做什么 created() { this.options = ['inited'] } }
this的使用
对于独立使用的函数,可以声明指定的调用上下文
class handler { info: string; // 声明指定的this上下文 onclickbad(this: handler, e: event) { // oops, used this here. using this callback would crash at runtime this.info = e.message; } } let h = new handler(); uielement.addclicklistener(h.onclickbad); // error!
声明合并(扩展vue声明)
来看看使用场景,扩展vue,在vue上添加全局的属性。
// vue的声明在 vue/types/vue.d.ts declare module 'vue/types/vue' { // 相当于vue.$eventbus interface vue { $eventbus: vue; } // 相当于在vue.prototype.$eventbus interface vueconstructor { $eventbus: vue; } }
总结
typescript声明还有很多高级的用法,目前我也没有用到那么多,在我纠结不会写声明的时候,我就会看看别人的声明文件时怎么写的。
注意:尽量不要把解构和声明写在一起,可读性极差。
class node { onnodecheck(checkedkeys: any, { // 解构 checked, checkednodes, node, event, } : { // 声明 node: any; [key: string]: any; } ) { } }
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: 公鸡的作用和功效
下一篇: JS实现提示框跟随鼠标移动