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 相关文章推荐
用js实现判断当前网址的来路如果不是指定的来路就跳转到指定页面
May 02 Javascript
通过一段代码简单说js中的this的使用
Jul 23 Javascript
jQuery 获取/设置/删除DOM元素的属性以a元素为例
May 23 Javascript
JavaScript小技巧整理篇(非常全)
Jan 26 Javascript
浅谈mint-ui loadmore组件注意的问题
Nov 08 Javascript
JS改变页面颜色源码分享
Feb 24 Javascript
js打开word文档预览操作示例【不是下载】
May 23 Javascript
JavaScript中将值转换为字符串的五种方法总结
Jun 06 Javascript
微信小程序页面间跳转传参方式总结
Jun 13 Javascript
vue中destroyed方法的使用说明
Jul 21 Javascript
JavaScript文档对象模型DOM
Nov 20 Javascript
JavaScript选择器函数querySelector和querySelectorAll
Nov 27 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
DOTA2 无惧惊涛骇浪 昆卡大型水友攻略
2020/04/20 DOTA
php将mysql数据库整库导出生成sql文件的具体实现
2014/01/08 PHP
利用php下载xls文件(自己动手写的)
2014/04/18 PHP
php环境无法上传文件的解决方法
2014/04/30 PHP
PHP中设置一个严格30分钟过期Session面试题的4种答案
2014/07/30 PHP
php+jquery+html实现点击不刷新加载更多的实例代码
2016/08/12 PHP
TP(thinkPHP)框架多层控制器和多级控制器的使用示例
2018/06/13 PHP
Mootools 1.2教程 定时器和哈希简介
2009/09/15 Javascript
JavaScript高级程序设计 读书笔记之十 本地对象Date日期
2012/02/27 Javascript
用JavaScript获取DOM元素位置和尺寸大小的方法
2013/04/12 Javascript
jQuery移除元素自动解绑事件实现思路及代码
2014/05/31 Javascript
javascript中return,return true,return false三者的用法及区别
2015/11/17 Javascript
bootstrap datepicker 与bootstrapValidator同时使用时选择日期后无法正常触发校验的解决思路
2016/09/28 Javascript
jQuery实现花式轮播之圣诞节礼物传送效果
2016/12/25 Javascript
ES6概念 ymbol.for()方法
2016/12/25 Javascript
Vue.js使用$.ajax和vue-resource实现OAuth的注册、登录、注销和API调用
2017/05/10 Javascript
bootstrap table使用入门基本用法
2017/05/24 Javascript
js实现鼠标移动到图片产生遮罩效果
2017/10/21 Javascript
JS实现将链接生成二维码并转为图片的方法
2018/03/17 Javascript
Vue 通过自定义指令回顾v-内置指令(小结)
2018/09/03 Javascript
在Vue中获取组件声明时的name属性方法
2018/09/12 Javascript
Python(Django)项目与Apache的管理交互的方法
2018/05/16 Python
Python3 执行系统命令并获取实时回显功能
2019/07/09 Python
python如何将两张图片生成为全景图片
2020/03/05 Python
使用python图形模块turtle库绘制樱花、玫瑰、圣诞树代码实例
2020/03/16 Python
Python3.9新特性详解
2020/10/10 Python
Vision Direct比利时:在线订购隐形眼镜
2019/08/27 全球购物
一套软件测试笔试题
2014/07/25 面试题
调研座谈会发言材料
2014/08/23 职场文书
2014感恩节演讲稿大全
2014/10/11 职场文书
2014年检验员工作总结
2014/11/19 职场文书
2015年团队工作总结范文
2015/05/04 职场文书
初中班主任教育随笔
2015/08/15 职场文书
QT连接MYSQL数据库的详细步骤
2021/07/07 MySQL
python如何查找列表中元素的位置
2022/05/30 Python
Python+SeaTable实现计算两个日期间的工作日天数
2022/07/07 Python