骚年,你感觉过debug一年找不到问题,最后发现是变量名写错时的绝望吗? 骚年,你感觉过生产线上代码出现Uncaught TypeError
时的恐惧吗? 骚年,你感觉过写代码找一万个文件还找不到方法定义时委屈吗?html
拿起键盘,让咱们对谋害生命的代码拖进垃圾箱!(划掉)前端
本期:《ts安利指南》vue
第二期:《TS in JS 实践指北》java
据了解,目前有至关一部分同窗不想去学习ts,毕竟没(xue)时(bu)间(dong)。很不幸两个月前我也是其中的一员。在看到尤大大都用ts写vue3了,蠢蠢欲动的我当心翼翼的踏入了这个深坑。在经历了长达一天的摸爬滚打以后,领悟到了真谛jquery
通过了一段时间的理解以后,写了这篇文章,旨在给犹豫是否学习或者还在观望TypeScript的同窗作个使用ts的收益分析,但愿可以打动屏幕面前的你。webpack
ts难写吗?不难。最简单的作法三步就搞定。git
大功告成!程序员
(打人别打脸,还要靠它吃饭的…)es6
⬇️ ts初体验 github
ts是js的超集,意味着js自己的语法在ts里面也能跑的通。ts一方面是对js加上了不少条条框框的限制,另外一方面是拓展了js的一些能力,就像es6提供了那么多神奇的语法糖同样。只要按照必定的规则去书写js,就能享受到ts带来的好处。
固然由于如今的ts足够强大,而且有自家的vscode保驾护航,才方便了咱们这些过去想都不(lan)敢(de)想的苦逼程序员。
js改形成ts的工做量很大程度取决于你想对本身的代码限制的有多细致,描述的有多完善。最简单的就像上面说的,改个拓展名就好了(固然很大程度上可能会经过不了各类静态检查)。若是你写的越多,用你代码的同志就越大可能喜欢你写的东西。
下面先简单介绍一下ts语法,便于后面的理解。
// 'xxx: number' 表示声明一个number类型 const num: number = 123 // 声明一个函数的参数类型(number以及any)和返回值(void) function fn (arg1: number, arg2: any): void { // todo } fn(num, [1,2,3,4]) // 声明一个接口 interface IPerson { name: string // IPerson须要包含一个name属性,类型是string age: number // IPerson须要包含一个age属性,类型是number family: string[] // IPerson须要包含一个family属性,类型是数组,数组里面都是string类型的数据 sex?: '男' | '女' // IPerson可选一个sex属性,值为'男'或者'女'或者undefined } // 使用IPerson接口定义一个对象,若是对象不符合IPerson的定义,编译器会飘红报错 const person: IPerson = { name: '小王', age: 12, family: ['爹', '娘'], } // type相似interface,如下写法等同用interface声明IPerson type IPerson2 = { name: string age: number family: string[] sex?: '男' | '女' } // 所以能够直接定义过来 const person2: IPerson2 = person 复制代码
可能有的同窗看了上面的介绍,会说:
"要写这么多其余代码,还增长了文件体积,搞个啥子咧"
通常状况下,ts须要编译成js才能运行。编译后长这样:
// 'xxx: number' 表示声明一个number类型 var num = 123; // 声明一个函数的参数类型(number以及any)和返回值(void) function fn(arg1, arg2) { // todo } fn(num, [1, 2, 3, 4]); // 使用IPerson接口定义一个对象,若是对象不符合IPerson的定义,编译器会飘红报错 var person = { name: '小王', age: 12, family: ['爹', '娘'], }; // 所以能够直接定义过来 var person2 = person; 复制代码
经过人肉diff,发现编译后的去掉了ts的全部代码。
可能就又有同窗想问了:
"学这些有啥好处?"
别急,接着往下看🤓
这块介绍ts的几个应用场景,给点启发~
平时为了代码的健壮性,不得不对代码作不少容错的操做。
假如成功避免了由于本身年龄大了而眼睛花了,使用本身写的方法时这里漏了一个参数,那里传错了参数类型。 常常会有些不靠谱的使用者,不看你辛辛苦苦耕耘的api文档,瞎jb传参。最后出了问题还怪你没有作好兼容处理,领导群里一顿数落。
咱们就得像孩子他妈同样,考虑熊孩子会传些什么乱七八糟的东西进来,而后在代码里面加上各类分支。
如今用ts,就能够在传参的时候友好的提示出来“你写了个什么玩意”的意思。
首先用ts定义一个函数
interface IArgs {
name: string
age: string
}
function youFoo (arg1: string, arg2: 'a'|'b', arg3: IArgs) {
// 这里啥都不干,你传参吧
}
复制代码
假如同事小明这么写
youFoo('sss', 'c', { name: 'xiaoming', age: 18 }) 复制代码
他就会发现哪里好像不太对
第二个参数要求'a'或者'b',因而小明默默的改过来了,可是又发现
原来age
是要求传string
类型。
因而小明一边内心mmp一边改了过来。
平时在干活的时候,咱们通常喜欢多一个屏幕,能够开个chrome,查查问题找找文档等。不过常常还得看网速,用搜索去搜api啥的,遇到在乡下写代码,分分钟有想shi的心。
有了ts,咱们就完(da)美(gai)的决掉了这个问题:
首先按照这样的结构去写方法:
/**
* 一个方法:生成错误提示信息
*
* @param {string} message 提示信息,好比`you have a error`
* @param {number | string} code 错误码,数字和字符都行
* @param {string} type 类型,请写`demo1`或者`demo2`
*
* [还不懂?点这里吧](https://www.google.com)
*
* ```js
* // demo
* genErrMsg('demo', 10086)
*
* ```
*/
export function genErrMsg (message: string, code: number | string, type?: ('demo1' | 'demo2')): string {
return (message || `网络繁忙,请稍候再试`) + (code ? `(${code})` : ``)
}
复制代码
而后在使用过程当中的体验以下:
在更完善的lib当中,体验更佳,除了开头的jquery
外,还好比:
阅读如下js代码, 提问:分割线如下的代码有几处bug?
// careless.js let foooo = 1 let fooo = 1 let fooooooo = 1 let foo = 1 let foooooo = 1 let test = 12 const obj = { fn1 () {}, fn2 () {}, fn4 () {}, } /*************** 分割线如下的代码有哪些地方有bug? **************** */ obj.fn3() console.leg(fooooo) function test () { alert(tast) } 复制代码
/*
**
**
***** 答案分界线 *****
**
**
*/
是否是以为眼睛有点要瞎了?
试试把.js改为.ts
若是说以前的js代码还能凭眼神马上看出哪里不对,那么下面这些就没那么简单了
阅读如下js代码, 提问:代码有几处bug?
import * as utils from './utils' utils.genErrMsg(10086, 'this is error') // 上面提到的genErrMsg函数 let dom = window.document.getElementById('foo') dom.className = 'add' 复制代码
/*
**
**
***** 答案分界线 *****
**
**
*/
试试把.js改为.ts
可知问题以下:
1.genErrMsg
的第一个参数应该是string
2.getElementById
返回值还多是null
在维护代码的过程当中,可能常常遇到某个接口不知道有啥数据,一般这个时候咱们须要去查接口文档。然而当次数一多,或者后台大佬一坑起来,改了字段,可能会查到怀疑人生。
若是使用ts,可能手里的剧本就不同了
假若有个接口以下所示
咱们针对这个接口写出了以下ts代码:
interface IPriceData { /** 标识 */ cbf: string /** id */ id: string /** 市场价格 */ m: string /** 后台价 */ op: string /** 前台价 */ p: string } // 将IPriceData塞进数组里 type IPriceDataArray = IPriceData[] function getPrice () { // Promise的泛型参数使用了IPriceDataArray类型,then里面返回的数据就是IPriceDataArray类型 return new Promise<IPriceDataArray>((resolve, reject) => { $.get('https://xxxxxxx/prices/pgets?ids=P_100012&area=&source=', data => { resolve(data) }) }) } 复制代码
当调用getPrice
函数时,体验以下:
之后每次维护这段函数的时候都不须要去看文档啦。若是后台忽然改了字段,在检查的过程当中咱们能够立刻发现问题,而后拿着数据去质问:你tm改了东西让我来背锅...(此处省略1万个字)
众所周知,js里面的class就是个语法糖,想学强类型语言,写法又是个半吊子。
可是在ts当中,class被加强了(固然仍是个语法糖,只不过更甜了)
我们看图说话:
vscode中对ts下的共有属性、私有属性、保护属性和静态属性开了小灶,实例下只有公有属性才会被容许使用和提示出来。
另外ts还提供了enum语法糖:
enum HttpCode { /** 成功 */ '200_OK' = 200, /** 已生成了新的资源 */ '201_Created' = 201, /** 请求稍后会被处理 */ '202_Accepted' = 202, /** 资源已经不存在 */ '204_NoContent' = 204, /** 被请求的资源有一系列可供选择的回馈信息 */ '300_MultipleChoices' = 300, /** 永久性转移 */ '301_MovedPermanently' = 301, /** 暂时性转移 */ '302_MoveTemporarily' = 302, } HttpCode['200_OK'] HttpCode[200] 复制代码
相比简单对象定义的key-value,只能经过key去访问value,不能经过value访问key。可是在enum当中,正反均可以当作key来用。
编译后的代码有兴趣的同窗能够了解下~
"use strict"; var HttpCode; (function (HttpCode) { /** 成功 */ HttpCode[HttpCode["200_OK"] = 200] = "200_OK"; /** 已生成了新的资源 */ HttpCode[HttpCode["201_Created"] = 201] = "201_Created"; /** 请求稍后会被处理 */ HttpCode[HttpCode["202_Accepted"] = 202] = "202_Accepted"; /** 资源已经不存在 */ HttpCode[HttpCode["204_NoContent"] = 204] = "204_NoContent"; /** 被请求的资源有一系列可供选择的回馈信息 */ HttpCode[HttpCode["300_MultipleChoices"] = 300] = "300_MultipleChoices"; /** 永久性转移 */ HttpCode[HttpCode["301_MovedPermanently"] = 301] = "301_MovedPermanently"; /** 暂时性转移 */ HttpCode[HttpCode["302_MoveTemporarily"] = 302] = "302_MoveTemporarily"; })(HttpCode || (HttpCode = {})); HttpCode['200_OK']; HttpCode[200]; 复制代码
经过上面的几个栗子,大概能够看出使用了ts后,能够得到如下技能点:
以及对应的技术成本
维护者(包的做者) | 使用者 | |
---|---|---|
收益 | 清晰的函数参数/接口属性 静态检查 生成api文档 |
清晰的函数参数/接口属性 配合现代编辑器,各类提示 |
代价 | 标记类型 声明(interface/type) |
和某些库结合的不是很完美(没错,说的就是vue 2.x) |
这里提到的vue2.x因为ts先天能力的不足,致使vue的ts语法须要使用class风格(运行时会被转换回本来的vue构造函数的语法),和咱们平时熟悉的vue风格有些差别
这里是由于vue的this下的环境比较复杂,对于ide来讲须要在运行时才能肯定,所以在编写ts的时候须要手动去设置属性(好比props,data,methods等)到this下面,很是麻烦。早期ts并不支持手动编写this的做用域,后来专门为其设计了一个ThisType
的方法。
在上面的代码里用了class
的写法,自己全部须要的属性就在this下,规避了运行时才能肯定this下须要的做用域的问题。
另外一方面,因为ts提示能力比较局限,好比在函数场景中,若是数据来源是独立的对象,体验就会比较糟糕。
请阅读如下栗子(这一块稍微超纲了标题'安利'的范畴,不太理解的新同窗能够入坑之后再消化~)
interface IOptions { name: string age: number extra: { data: Object methods: Object } } // 参数options要求符合IOptions定义的规则 function sthConstructor (options: IOptions) {} // options对象当中并无任何ts的静态检查和提示 const options = { name: 'peter', age: '13', // error: age应该为数字 extra: { data: [], methods: {} } } // options飘红报错,然而提示内容废话太多,关键信息藏得太深 sthConstructor(options) 复制代码
在上面的场景,咱们但愿在options当中可以得到完整的ts检查能力。达成这个目的有三种方法:
1.将options里面的东西挪进函数当中
2.将options
用IObject定义
3.提供一个helper方法
这三种方式当中:
方法1是最简单的方式,可是在大型项目当中,这样的写法反而不多见到。
方法2是维护者经常使用的方式,可是对于使用者而言,成本较高。由于使用者须要去lib里翻到方法对应的type类型,将它import进来。
方法3是我的以为相对比较好的方式,只要维护者提供一个相似helper
的函数包装一下,就能够得到对应的提示。是否是很像vue ts的装饰器?
但上述三种解决方式我以为都不优雅,这就是ts当前的不足之一。
TypeScript是和vscode都是微软的亲儿子,他们兄弟俩相互协做确定会有更多小花样,甚至你用的只是js文件,也能够享受到。
这里抛砖引玉列出两条:
只要有types文件,全部配置均可以自动提示:
/** * webpack配置自动提示 * * 先安装对应的types包: `npm i @types/webpack -D` * * @type {import('webpack').Configuration} */ const config = { } 复制代码
在js中也能够得到自动提示和静态检查。只要在vscode的setting当中勾上Check JS
便可。虽然你的js代码可能会被各类飘红🤪
⬇️ 以前的例子在js中也能够提示出一些bug了
有的同窗会问:我才学js,能够学ts吗?能够,而且建议,由于会对js基础知识加深理解。有用法问题在stackoverflow上搜搜就解决了。
那么这么有用的工具,去哪能够学到呢?或许你能够参考下我学习的轨迹:
传送门--为 Vue3 学点 TypeScript , 体验 TypeScript
传送门--一篇朴实的文章带你30分钟捋完TypeScript,方法是正反对比
今年ts忽然遍地开花,彷佛成为了潮流。各类ts改造、学习教程、心得出如今了各大学习、交友网站上。 有的同窗可能也发现了:这不就就是java这类语言玩剩了的东西了吗?
那年轻的时候谁不都想自由嘛,然而随着年龄大了都被管的服服帖帖的
感谢 @俊宁 @zenghongtu 评论中指点
文章中本来使用了Array泛型(Array<xxx>
)部分表示数组的部分已经所有改成方括号(xxx[]
)表示。
缘由是3.4后的ts在readonly场景下用Array泛型表示数组会有警告。都改成使用方括号方式能够避免在复杂场景踩坑。
// readonly修饰只能用于方括号的数组和元组上 let err1: readonly Set<number>; // 错误! let err2: readonly Array<boolean>; // 错误! let okay: readonly boolean[]; // 无错误 let okay2: readonly [boolean, string]; // 无错误 复制代码
特此注释。
若是你以为这篇内容对你有价值,欢迎点赞并关注咱们前端团队的官网和咱们的微信公众号(WecTeam),每周都有优质文章推送~