騷年,你感覺過debug一年找不到問題,最後發現是變量名寫錯時的絕望嗎? 騷年,你感覺過生產線上代碼出現Uncaught TypeError
時的恐懼嗎? 騷年,你感覺過寫代碼找一萬個文件還找不到方法定義時委屈嗎?html
拿起鍵盤,讓咱們對謀害生命的代碼拖進垃圾箱!(劃掉)前端
據瞭解,目前有至關一部分同窗不想去學習ts,畢竟沒(xue)時(bu)間(dong)。很不幸兩個月前我也是其中的一員。在看到尤大大都用ts寫vue3了,蠢蠢欲動的我當心翼翼的踏入了這個深坑。在經歷了長達一天的摸爬滾打以後,領悟到了真諦vue
通過了一段時間的理解以後,寫了這篇文章,旨在給猶豫是否學習或者還在觀望TypeScript的同窗作個使用ts的收益分析,但願可以打動屏幕面前的你。java
ts難寫嗎?不難。最簡單的作法三步就搞定。jquery
大功告成!webpack
(打人別打臉,還要靠它吃飯的…)git
⬇️ ts初體驗 程序員
ts是js的超集,意味着js自己的語法在ts裏面也能跑的通。ts一方面是對js加上了不少條條框框的限制,另外一方面是拓展了js的一些能力,就像es6提供了那麼多神奇的語法糖同樣。只要按照必定的規則去書寫js,就能享受到ts帶來的好處。es6
固然由於如今的ts足夠強大,而且有自家的vscode保駕護航,才方便了咱們這些過去想都不(lan)敢(de)想的苦逼程序員。github
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),每週都有優質文章推送~