騷年,你感覺過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),每週都有優質文章推送~