TypeScript安利指南

騷年,你感覺過debug一年找不到問題,最後發現是變量名寫錯時的絕望嗎? 騷年,你感覺過生產線上代碼出現Uncaught TypeError時的恐懼嗎? 騷年,你感覺過寫代碼找一萬個文件還找不到方法定義時委屈嗎?html

拿起鍵盤,讓咱們對謀害生命的代碼拖進垃圾箱!(劃掉)前端

前言

據瞭解,目前有至關一部分同窗不想去學習ts,畢竟沒(xue)時(bu)間(dong)。很不幸兩個月前我也是其中的一員。在看到尤大大都用ts寫vue3了,蠢蠢欲動的我當心翼翼的踏入了這個深坑。在經歷了長達一天的摸爬滾打以後,領悟到了真諦vue

真香

通過了一段時間的理解以後,寫了這篇文章,旨在給猶豫是否學習或者還在觀望TypeScript的同窗作個使用ts的收益分析,但願可以打動屏幕面前的你。java

安利

ts難寫嗎?不難。最簡單的作法三步就搞定。jquery

  1. 找一個js文件
  2. 按下重命名
  3. 把.js改爲.ts

大功告成!webpack

打臉

(打人別打臉,還要靠它吃飯的…)git

⬇️ ts初體驗 程序員

gif0

-ts是什麼

ts是js的超集,意味着js自己的語法在ts裏面也能跑的通。ts一方面是對js加上了不少條條框框的限制,另外一方面是拓展了js的一些能力,就像es6提供了那麼多神奇的語法糖同樣。只要按照必定的規則去書寫js,就能享受到ts帶來的好處。es6

固然由於如今的ts足夠強大,而且有自家的vscode保駕護航,才方便了咱們這些過去想都不(lan)敢(de)想的苦逼程序員。github

js改形成ts的工做量很大程度取決於你想對本身的代碼限制的有多細緻,描述的有多完善。最簡單的就像上面說的,改個拓展名就好了(固然很大程度上可能會經過不了各類靜態檢查)。若是你寫的越多,用你代碼的同志就越大可能喜歡你寫的東西。

下面先簡單介紹一下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,就能夠在傳參的時候友好的提示出來「你寫了個什麼玩意」的意思。

-w149

首先用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萬個字)

-加強後的class和enum

衆所周知,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文檔
  • 配合現代編輯器,各類提示
  • 活躍的社區

以及對應的技術成本

維護者(包的做者) 使用者
收益 清晰的函數參數/接口屬性
靜態檢查
生成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當前的不足之一。

ts在js中的玩法

TypeScript是和vscode都是微軟的親兒子,他們兄弟倆相互協做確定會有更多小花樣,甚至你用的只是js文件,也能夠享受到。

這裏拋磚引玉列出兩條:

-配置文件自動提示

只要有types文件,全部配置均可以自動提示:

/**
 * webpack配置自動提示
 * 
 * 先安裝對應的types包: `npm i @types/webpack -D`
 * 
 * @type {import('webpack').Configuration}
 */
const config = {
    
}
複製代碼

-js語法檢查

在js中也能夠得到自動提示和靜態檢查。只要在vscode的setting當中勾上Check JS便可。雖然你的js代碼可能會被各類飄紅🤪

⬇️ 以前的例子在js中也能夠提示出一些bug了

寫在最後

有的同窗會問:我才學js,能夠學ts嗎?能夠,而且建議,由於會對js基礎知識加深理解。有用法問題在stackoverflow上搜搜就解決了。

那麼這麼有用的工具,去哪能夠學到呢?或許你能夠參考下我學習的軌跡:

傳送門--TypeScript 入門教程 (牆裂推薦)

傳送門--爲 Vue3 學點 TypeScript , 體驗 TypeScript

傳送門--一篇樸實的文章帶你30分鐘捋完TypeScript,方法是正反對比

傳送門--stack overflow (牆裂推薦)

傳送門--google

今年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),每週都有優質文章推送~

WecTeam
相關文章
相關標籤/搜索