经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » TypeScript » 查看文章
TypeScript类型声明书写详解
来源:jb51  时间:2019/8/28 11:27:32  对本文有异议

本文总结一下TypeScript类型声明的书写,很多时候写TypeScript不是问题,写类型就特别纠结,我总结下,我在使用TypeScript中遇到的问题。如果你遇到类型声明不会写的时候,多看看lodash的声明,因为lodash对数据进行各种变形操作,所以你能遇到的,都有参考示例。

基本类型

  1. // 变量
  2. const num: number = 1;
  3. const str: string = 'str';
  4. const bool: boolean = true;
  5.  
  6. const nulls: null = null;
  7. const undefine: undefined = undefined;
  8. const symbols: symbol = Symbol('symbal');
  9.  
  10. const any: any = 'any types'; // typescript的any类型,相当于什么类型约束都没有
  11.  

数组

  1. // 数组: 推荐使用T[]这种写法
  2. const nums: number[] = [1, 2, 3, 4];
  3.  
  4. // 不推荐:Array<T>泛型写法,因为在JSX中不兼容,所以为了统一都使用T[]这种类型
  5. const strs: Array<string> = ['s', 't', 'r'];
  6.  
  7. const dates: Date[] = [new Date(), new Date()];
  8.  

数组的concat方法,返回类型为never[]问题

  1. // 数组concat方法的never问题
  2. // 提示: Type 'string' is not assignable to type 'never'.
  3. const arrNever: string[] = [].concat(['s']);
  4.  
  5. // 主要问题是:[]数组,ts无法根据上下文判断数组内部元素的类型
  6. // @see https://github.com/Microsoft/TypeScript/issues/10479
  7. const fixArrNever: string[] = ([] as string[]).concat(['s']);
  8.  

接口

接口是 TypeScript 的一个核心知识,它能合并众多类型声明至一个类型声明:

而且接口可以用来声明:函数,类,对象等数据类型

  1. interface Name {
  2. first: string;
  3. second: string;
  4. }
  5.  
  6. let username: Name = {
  7. first: 'John',
  8. second: 'Doe'
  9. };
  10.  

any、null、undefined、void类型

  1. // 特殊类型
  2. const any: any = 'any types'; // typescript的any类型,相当于什么类型都没写
  3. let nobody: any = 'nobody, but you';
  4. nobody = 123;
  5.  
  6. let nulls: number = null;
  7. let bool: boolean = undefined;
  8.  
  9. // void
  10. function printUsername (name: string): void {
  11. console.log(name);
  12. }
  13.  

联合类型

联合类型在option bags模式场景非常实用,使用 **| **来做标记

  1. function options(opts: {
  2. types?: string;
  3. tag: string | number;
  4. }): void {
  5. }
  6.  

交叉类型

最典型的使用场景就是继承和mixin,或者copy等操作

  1. // 交叉类型:如果以后遇到此种类型声明不会写,直接看Object.assign声明写法
  2. function $extend<T, U>(first: T, second: U): T & U {
  3. return Object.assign(first, second); // 示意而已
  4. }
  5.  

元组 tuple

元组很少使用

  1. let nameNumber: [string, number];
  2.  
  3. // Ok
  4. nameNumber = ['Jenny', 221345];
  5.  
  6. // Error
  7. // nameNumber = ['Jenny', '221345'];
  8.  
  9. let tuple: [string, number];
  10. nameNumber = ['Jenny', 322134];
  11.  
  12. const [usernameStr, uselessNum] = nameNumber;
  13.  

type的作用

ype用来创建新的类型,也可以重命名(别名)已有的类型,建议使用type创建简单类型,无嵌套的或者一层嵌套的类型,其它复杂的类型都应该使用interface, 结合implements ,extends实现。

  1. type StrOrNum = string | number;
  2.  
  3. // 使用
  4. let sample: StrOrNum;
  5. sample = 123;
  6. sample = '123';
  7.  
  8. // 会检查类型
  9. sample = true; // Error
  10.  

实践中遇到的问题

第三方库没有提供声明d.ts文件

如果第三方库没有提供声明文件,第一时间去微软官方的仓库https://github.com/borisyankov/DefinitelyTyped 查找,或者在npmjs.com上搜索@types/依赖的模块名大部分情况都可以找到。

手动添加声明文件

声明文件一般都是统一放置在types文件夹下

  1. // 例子: types/axios.d.ts
  2. declare module 'axios'; // 这里的axios声明为any类型
  3.  

全局变量

例如一些库直接把在window上添加的全局变量

  1. // globals.d.ts
  2. // 例子:jQuery,现实中jQuery是有.d.ts
  3. declare const jQuery: any;
  4. declare const $: typeof jQuery;
  5.  

非JavaScript资源

在前端工程中,import很多非js资源,例如:css, html, 图片,vue, 这种ts无法识别的资源时,就需要告诉ts,怎么识别这些导入的资源的类型。

  1. // 看看vue怎么处理的:shims-vue.d.ts
  2. declare module '*.vue' {
  3. import Vue from 'vue';
  4. export default Vue;
  5. }
  6.  
  7. // html
  8. declare module '*.html';
  9. // css
  10. declare module '*.css';
  11.  

强制类型转换

有时候遇到需要强制类型转换,尤其是对联合类型或者可选属性时。

  1. // 第一种:使用<>括号
  2. const convertArrType: string[] = <Array<string>>[].concat(['s']);
  3.  
  4. // 第二种:使用as关键字
  5. const fixArrNever: string[] = ([] as string[]).concat(['s']);
  6.  

建议使用第二种,因为兼容JSX,第一种官方也不推荐了,虽然它是合法的。

可选属性和默认属性

API中提供的参数很多都有默认值,或者属性可选,怎么书写呢?

  1. class Socket {}
  2. // 联合类型
  3. export type SocketType = 'WebSocket' | 'SockJs';
  4.  
  5. export interface SocketOptions {
  6. type: SocketType;
  7. protocols?: string | string[]; // 可选
  8. pingMessage: string | (() => string); // 联合类型,可以为string或者函数
  9. pongMessage: string | (() => string);
  10. }
  11.  
  12. // 默认值
  13. export function eventHandler = (
  14. evt: CloseEvent | MessageEvent | Event,
  15. socket: Socket,
  16. type = 'WebSocket' // 默认值
  17. ) => any;
  18.  

独立函数怎么声明类型

刚开始我也很纠结这个问题,我就是一个独立的函数,怎么声明类型呢?尤其是写事件处理函数的API时。

  1. class Socket {}
  2. // 函数的声明方式
  3. export type SocketEventHandler = (
  4. evt: CloseEvent | MessageEvent | Event,
  5. socket: Socket
  6. ) => any;
  7.  
  8. const eventHandler: SocketEventHandler = (evt, socket) => {
  9. }
  10.  
  11. // 可选参数和rest参数
  12. let baz = (x = 1) => {};
  13. let foo = (x: number, y: number) => {};
  14. let bar = (x?: number, y?: number) => {};
  15. let bas = (...args: number[]) => {};
  16.  

索引属性类型声明

JavaScript中的对象都可以使用字符串索引直接取属性或者调用方法,TypeScript中也有相应的类型声明方法。

  1. type Hello = {
  2. hello: 'world';
  3. // key只是一个形式属性名(类似形参一样)
  4. [key: string]: string;
  5. };
  6.  
  7. const greeting: Hello = {
  8. hi: 'morning'
  9. }
  10.  
  11. console.log(greeting['hi'])
  12.  

动态添加的属性声明

有的时候我们只声明了一个基本的类型结构,然后后续有扩展的情况 ,尤其时二次封装时的options。

  1. interface AxiosOptions {}
  2.  
  3. type AjaxOptions = {
  4. axiosOptions: AxiosOptions;
  5. // 额外扩展的放入到单独的属性节点下
  6. extraOptions: {
  7. [prop: string]: any
  8. };
  9. };
  10.  
  11. type AjaxOptions1 = {
  12. axiosOptions?: AxiosOptions;
  13. // 不要这样写,因为axiosOptions拼写错误时,ts不会提示
  14. // 尽量把后续扩展的属性,移动到独立的属性节点下
  15. [prop: string]: any
  16. };
  17.  
  18. const ajaxOptions: AjaxOptions1 = {
  19. axiosOptions1: {}; // 本意是axiosOptions,但是ts不会提示
  20. }
  21.  

!的使用

! 标识符告诉ts编译器,声明的变量没有问题,再运行期不会报错。

  1. class BaseSelect extends Vue {
  2. options: string[]; // 这里会提示没有在constructor中初始化
  3. created() {
  4. this.options = ['inited']
  5. }
  6. }
  7.  
  8.  
  9. class BaseSelect extends Vue {
  10. options!: string[]; // 使用 ! 告诉编译器,我知道自己在做什么
  11. created() {
  12. this.options = ['inited']
  13. }
  14. }
  15.  

this的使用

对于独立使用的函数,可以声明指定的调用上下文

  1. class Handler {
  2. info: string;
  3. // 声明指定的this上下文
  4. onClickBad(this: Handler, e: Event) {
  5. // oops, used this here. using this callback would crash at runtime
  6. this.info = e.message;
  7. }
  8. }
  9. let h = new Handler();
  10. uiElement.addClickListener(h.onClickBad); // error!
  11.  

声明合并(扩展Vue声明)

来看看使用场景,扩展vue,在vue上添加全局的属性。

  1. // vue的声明在 vue/types/vue.d.ts
  2.  
  3. declare module 'vue/types/vue' {
  4. // 相当于Vue.$eventBus
  5. interface Vue {
  6. $eventBus: Vue;
  7. }
  8. // 相当于在Vue.prototype.$eventBus
  9. interface VueConstructor {
  10. $eventBus: Vue;
  11. }
  12. }
  13.  

总结

TypeScript声明还有很多高级的用法,目前我也没有用到那么多,在我纠结不会写声明的时候,我就会看看别人的声明文件时怎么写的。

注意:尽量不要把解构和声明写在一起,可读性极差。

  1. class Node {
  2. onNodeCheck(checkedKeys: any, { // 解构
  3. checked, checkedNodes, node, event,
  4. } : { // 声明
  5. node: any;
  6. [key: string]: any;
  7. }
  8. ) {
  9. }
  10. }

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

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号