TypeScript类型声明书写详解


Posted in Javascript onAugust 28, 2019

本文总结一下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;
  }
 ) { 
 }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
JavaScript 5 新增 Array 方法实现介绍
Feb 06 Javascript
js操作checkbox遇到的问题解决
Jun 29 Javascript
使用非html5实现js板连连看游戏示例代码
Sep 22 Javascript
jQuery菜单插件superfish使用指南
Apr 21 Javascript
在Javascript中处理字符串之big()方法的使用
Jun 08 Javascript
简单掌握JavaScript中const声明常量与变量的用法
May 21 Javascript
js倒计时简单实现代码
Aug 11 Javascript
用jQuery实现优酷首页轮播图
Jan 09 Javascript
神级程序员JavaScript300行代码搞定汉字转拼音
May 20 Javascript
在vue-cli中组件通信的方法
Dec 16 Javascript
JavaScript数据结构与算法之检索算法实例分析【顺序查找、最大最小值、自组织查询】
Feb 22 Javascript
使用vue开发移动端管理后台的注意事项
Mar 07 Javascript
vue服务端渲染操作简单入门实例分析
Aug 28 #Javascript
浅谈对于“不用setInterval,用setTimeout”的理解
Aug 28 #Javascript
Vue的编码技巧与规范使用详解
Aug 28 #Javascript
JS开发自己的类库实例分析
Aug 28 #Javascript
详解Vue 换肤方案验证
Aug 28 #Javascript
Vue项目实现换肤功能的一种方案分析
Aug 28 #Javascript
js遍历详解(forEach, map, for, for...in, for...of)
Aug 28 #Javascript
You might like
微信支付开发维权通知实例
2016/07/12 PHP
基于PHP实现短信验证码接口(容联运通讯)
2016/09/06 PHP
js prototype截取字符串函数
2010/04/01 Javascript
解决javascript:window.close()在chrome,Firefox下失效的问题
2013/05/07 Javascript
基于JavaScript实现继承机制之构造函数+原型链混合方式的使用详解
2013/05/07 Javascript
jQuery实现类似老虎机滚动抽奖效果
2015/08/06 Javascript
JavaScript中的数据类型转换方法小结
2015/10/26 Javascript
js实现的彩色方块飞舞奇幻效果
2016/01/27 Javascript
mvc 、bootstrap 结合分布式图简单实现分页
2016/10/10 Javascript
微信小程序 登录实例详解
2017/01/16 Javascript
php main 与 iframe 相互通讯类(js+php同域/跨域)
2017/09/14 Javascript
Vue.js中使用iView日期选择器并设置开始时间结束时间校验功能
2018/08/12 Javascript
vue中的计算属性实例详解
2018/09/19 Javascript
Vue+Express实现登录注销功能的实例代码
2019/05/05 Javascript
Angular 2使用路由自定义弹出组件toast操作示例
2019/05/10 Javascript
webpack4 SplitChunks实现代码分隔详解
2019/05/23 Javascript
javascript写一个ajax自动拦截并下载数据代码实例
2019/09/07 Javascript
vue中实现点击变成全屏的多种方法
2020/09/27 Javascript
[02:10]2018DOTA2亚洲邀请赛赛前采访-Liquid
2018/04/03 DOTA
python验证码识别教程之利用滴水算法分割图片
2018/06/05 Python
python导入坐标点的具体操作
2019/05/10 Python
Python学习笔记之函数的定义和作用域实例详解
2019/08/13 Python
Python之Numpy的超实用基础详细教程
2019/10/23 Python
Python笔记之代理模式
2019/11/20 Python
Python内建序列通用操作6种实现方法
2020/03/26 Python
通过代码实例了解Python3编程技巧
2020/10/13 Python
德国孕妇装和婴童服装网上商店:bellybutton
2018/04/12 全球购物
Linux内核的同步机制是什么?主要有哪几种内核锁
2013/01/03 面试题
Shell编程面试题
2016/05/29 面试题
国贸类专业毕业生的求职信分享
2013/12/08 职场文书
十岁生日家长答谢词
2014/01/17 职场文书
材料专业毕业生求职信
2014/02/26 职场文书
机械设计及其自动化专业求职信
2014/06/09 职场文书
公司领导班子对照检查材料
2014/09/24 职场文书
中班下学期个人总结
2015/02/12 职场文书
女性励志书籍推荐
2019/08/19 职场文书