成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

精讀《Typescript2.0 - 2.9》

william / 1198人閱讀

摘要:比如或者都會導致函數(shù)返回值類型時。和特性一樣,等于是函數(shù)返回值中的或。注意對比下面的寫法對于,它的返回值是可迭代的對象,并且每個類型都是或者。首先是不支持方法重載的,是支持的,而類型系統(tǒng)一定程度在對標,當然要支持這個功能。

1 引言

精讀原文是 typescript 2.0-2.9 的文檔:

2.0-2.8,2.9 草案.

我發(fā)現(xiàn),許多寫了一年以上 Typescript 開發(fā)者,對 Typescript 對理解和使用水平都停留在入門階段。造成這個現(xiàn)象的原因是,Typescript 知識的積累需要 刻意練習,使用 Typescript 的時間與對它的了解程度幾乎沒有關(guān)系。

這篇文章精選了 TS 在 2.0-2.9 版本中最重要的功能,并配合實際案例解讀,幫助你快速跟上 TS 的更新節(jié)奏。

對于 TS 內(nèi)部優(yōu)化的用戶無感部分并不會羅列出來,因為這些優(yōu)化都可在日常使用過程中感受到。

2 精讀

由于 Typescript 在嚴格模式下的許多表現(xiàn)都與非嚴格模式不同,為了避免不必要的記憶,建議只記嚴格模式就好了!

嚴格模式導致的大量邊界檢測代碼,已經(jīng)有解了

直接訪問一個變量的屬性時,如果這個變量是 undefined,不但屬性訪問不到,js 還會拋出異常,這幾乎是業(yè)務(wù)開發(fā)中最高頻的報錯了(往往是后端數(shù)據(jù)異常導致的),而 typescript 的 strict 模式會檢查這種情況,不允許不安全的代碼出現(xiàn)。

2.0 版本,提供了 “非空斷言標志符” !. 解決明確不會報錯的情況,比如配置文件是靜態(tài)的,那肯定不會拋出異常,但在 2.0 之前的版本,我們可能要這么調(diào)用對象:

const config = {
  port: 8000
};

if (config) {
  console.log(config.port);
}

有了 2.0 提供的 “非空斷言標志符”,我們可以這么寫了:

console.log(config!.port);

2.8 版本,ts 支持了條件類型語法:

type TypeName = T extends string ? "string"

當 T 的類型是 string 時,TypeName 的表達式類型為 "string"。

這這時可以構(gòu)造一個自動 “非空斷言” 的類型,把代碼簡化為:

console.log(config.port);

前提是框架先把 config 指定為這個特殊類型,這個特殊類型的定義如下:

export type PowerPartial = {
  [U in keyof T]?: T[U] extends object ? PowerPartial : T[U]
};

也就是 2.8 的條件類型允許我們在類型判斷進行遞歸,把所有對象的 key 都包一層 “非空斷言”!

此處靈感來自 egg-ts 總結(jié)
增加了 never object 類型

當一個函數(shù)無法執(zhí)行完,或者理解為中途中斷時,TS 2.0 認為它是 never 類型。

比如 throw Error 或者 while(true) 都會導致函數(shù)返回值類型時 never。

null undefined 特性一樣,never 等于是函數(shù)返回值中的 nullundefined它們都是子類型,比如類型 number 自帶了 nullundefined 這兩個子類型,是因為任何有類型的值都有可能是空(也就是執(zhí)行期間可能沒有值)。

這里涉及到很重要的概念,就是預定義了類型不代表類型一定如預期,就好比函數(shù)運行時可能因為 throw Error 而中斷。所以 ts 為了處理這種情況,null undefined 設(shè)定為了所有類型的子類型,而從 2.0 開始,函數(shù)的返回值類型又多了一種子類型 never。

TS 2.2 支持了 object 類型, 但許多時候我們總把 objectany 類型弄混淆,比如下面的代碼:

const persion: object = {
  age: 5
};
console.log(persion.age); // Error: Property "age" does not exist on type "object".

這時候報錯會出現(xiàn),有時候閉個眼改成 any 就完事了。其實這時候只要把 object 刪掉,換成 TS 的自動推導就搞定了。那么問題出在哪里?

首先 object 不是這么用的,它是 TS 2.3 版本中加入的,用來描述一種非基礎(chǔ)類型,所以一般用在類型校驗上,比如作為參數(shù)類型。如果參數(shù)類型是 object,那么允許任何對象數(shù)據(jù)傳入,但不允許 3 "abc" 這種非對象類型:

declare function create(o: object | null): void;

create({ prop: 0 }); // 正確
create(null); // 正確

create(42); // 錯誤
create("string"); // 錯誤
create(false); // 錯誤
create(undefined); // 錯誤

而一開始 const persion: object 這種用法,是將能精確推導的對象類型,擴大到了整體的,模糊的對象類型,TS 自然無法推斷這個對象擁有哪些 key,因為對象類型僅表示它是一個對象類型,在將對象作為整體觀察時是成立的,但是 object 類型是不承認任何具體的 key 的。

增加了修飾類型

TS 在 2.0 版本支持了 readonly 修飾符,被它修飾的變量無法被修改。

在 TS 2.8 版本,又增加了 -+ 修飾修飾符,有點像副詞作用于形容詞。舉個例子,readonly 就是 +readonly,我們也可以使用 -readonly 移除只讀的特性;也可以通過 -?: 的方式移除可選類型,因此可以延伸出一種新類型:Required,將對象所有可選修飾移除,自然就成為了必選類型:

type Required = { [P in keyof T]-?: T[P] };
可以定義函數(shù)的 this 類型

也是 TS 2.0 版本中,我們可以定制 this 的類型,這個在 vue 框架中尤為有用:

function f(this: void) {
  // make sure `this` is unusable in this standalone function
}

this 類型是一種假參數(shù),所以并不會影響函數(shù)真正參數(shù)數(shù)量與位置,只不過它定義在參數(shù)位置上,而且永遠會插隊在第一個。

引用、尋址支持通配符了

簡單來說,就是模塊名可以用 * 表示任何單詞了:

declare module "*!text" {
  const content: string;
  export default content;
}

它的類型可以輻射到:

import fileContent from "./xyz.txt!text";

這個特性很強大的一個點是用在拓展模塊上,因為包括 tsconfig.json 的模塊查找也支持通配符了!舉個例子一下就懂:

最近比較火的 umi 框架,它有一個 locale 插件,只要安裝了這個插件,就可以從 umi/locale 獲取國際化內(nèi)容:

import { locale } from "umi/locale";

其實它的實現(xiàn)是創(chuàng)建了一個文件,通過 webpack.alias 將引用指了過去。這個做法非常棒,那么如何為它加上類型支持呢?只要這么配置 tsconfig.json:

{
  "compilerOptions": {
    "paths": {
      "umi/*": ["umi", ""]
    }
  }
}

將所有 umi/* 的類型都指向 ,那么 umi/locale 就會指向 /locale.ts 這個文件,如果插件自動創(chuàng)建的文件名也恰好叫 locale.ts,那么類型就自動對應上了。

跳過倉庫類型報錯

TS 在 2.x 支持了許多新 compileOptions,但 skipLibCheck 實在是太耀眼了,筆者必須多帶帶提出來說。

skipLibCheck 這個屬性不但可以忽略 npm 不規(guī)范帶來的報錯,還能最大限度的支持類型系統(tǒng),可謂一舉兩得。

拿某 UI 庫舉例,某天發(fā)布的小版本 d.ts 文件出現(xiàn)一個漏洞,導致整個項目構(gòu)建失敗,你不再需要提 PR 催促作者修復了!skipLibCheck 可以忽略這種報錯,同時還能保持類型的自動推導,也就是說這比 declare module "ui-lib" 將類型設(shè)置為 any 更強大。

對類型修飾的增強

TS 2.1 版本可謂是針對類型操作革命性的版本,我們可以通過 keyof 拿到對象 key 的類型:

interface Person {
  name: string;
  age: number;
}

type K1 = keyof Person; // "name" | "age"

基于 keyof,我們可以增強對象的類型:

type NewObjType = { [P in keyof T]: T[P] };

Tips:在 TS 2.8 版本,我們可以以表達式作為 keyof 的參數(shù),比如 keyof (A & B)。
Tips:在 TS 2.9 版本,keyof 可能返回非 string 類型的值,因此從一開始就不要認為 keyof 的返回類型一定是 string。

NewObjType 原封不動的將對象類型重新描述了一遍,這看上去沒什么意義。但實際上我們有三處拓展的地方:

左邊:比如可以通過 readonly 修飾,將對象的屬性變成只讀。

中間:比如將 : 改成 ?:,將對象所有屬性變成可選。

右邊:比如套一層 Promise,將對象每個 keyvalue 類型覆蓋。

基于這些能力,我們拓展出一系列上層很有用的 interface

Readonly。把對象 key 全部設(shè)置為只讀,或者利用 2.8 的條件類型語法,實現(xiàn)遞歸設(shè)置只讀。

Partial。把對象的 key 都設(shè)置為可選。

Pick。從對象類型 T 挑選一些屬性 K,比如對象擁有 10 個 key,只需要將 K 設(shè)置為 "name" | "age" 就可以生成僅支持這兩個 key 的新對象類型。

Extract。是 Pick 的底層 API,直到 2.8 版本才內(nèi)置進來,可以認為 Pick 是挑選對象的某些 key,Extract 是挑選 key 中的 key。

Record。將對象某些屬性轉(zhuǎn)換成另一個類型。比較常見用在回調(diào)場景,回調(diào)函數(shù)返回的類型會覆蓋對象每一個 key 的類型,此時類型系統(tǒng)需要 Record 接口才能完成推導。

Exclude。將 T 中的 U 類型排除,和 Extract 功能相反。

Omit(未內(nèi)置)。從對象 T 中排除 key 是 K 的屬性??梢岳脙?nèi)置類型方便推導出來:type Omit = Pick>

NonNullable。排除 Tnullundefined 的可能性。

ReturnType。獲取函數(shù) T 返回值的類型,這個類型意義很大。

InstanceType。獲取一個構(gòu)造函數(shù)類型的實例類型。

以上類型都內(nèi)置在 lib.d.ts 中,不需要定義就可直接使用,可以認為是 Typescript 的 utils 工具庫。

多帶帶拿 ReturnType 舉個例子,體現(xiàn)出其重要性:

Redux 的 Connect 第一個參數(shù)是 mapStateToProps,這些 Props 會自動與 React Props 聚合,我們可以利用 ReturnType 拿到當前 Connect 注入給 Props 的類型,就可以打通 Connect 與 React 組件的類型系統(tǒng)了。

對 Generators 和 async/await 的類型定義

TS 2.3 版本做了許多對 Generators 的增強,但實際上我們早已用 async/await 替代了它,所以 TS 對 Generators 的增強可以忽略。需要注意的一塊是對 for..of 語法的異步迭代支持:

async function f() {
  for await (const x of fn1()) {
    console.log(x);
  }
}

這可以對每一步進行異步迭代。注意對比下面的寫法:

async function f() {
  for (const x of await fn2()) {
    console.log(x);
  }
}

對于 fn1,它的返回值是可迭代的對象,并且每個 item 類型都是 Promise 或者 Generator。對于 fn2,它自身是個異步函數(shù),返回值是可迭代的,而且每個 item 都不是異步的。舉個例子:

function fn1() {
  return [Promise.resolve(1), Promise.resolve(2)];
}

function fn2() {
  return [1, 2];
}

在這里順帶一提,對 Array.map 的每一項進行異步等待的方法:

await Promise.all(
  arr.map(async item => {
    return await item.run();
  })
);

如果為了執(zhí)行順序,可以換成 for..of 的語法,因為數(shù)組類型是一種可迭代類型。

泛型默認參數(shù)

了解這個之前,先介紹一下 TS 2.0 之前就支持的函數(shù)類型重載。

首先 JS 是不支持方法重載的,Java 是支持的,而 TS 類型系統(tǒng)一定程度在對標 Java,當然要支持這個功能。好在 JS 有一些偏方實現(xiàn)偽方法重載,典型的是 redux 的 createStore

export default function createStore(reducer, preloadedState, enhancer) {
  if (typeof preloadedState === "function" && typeof enhancer === "undefined") {
    enhancer = preloadedState;
    preloadedState = undefined;
  }
}

既然 JS 有辦法支持方法重載,那 TS 補充了函數(shù)類型重載,兩者結(jié)合就等于 Java 方法重載:

declare function createStore(
  reducer: Reducer,
  preloadedState: PreloadedState,
  enhancer: Enhancer
);
declare function createStore(reducer: Reducer, enhancer: Enhancer);

可以清晰的看到,createStore 想表現(xiàn)的是對參數(shù)個數(shù)的重載,如果定義了函數(shù)類型重載,TS 會根據(jù)函數(shù)類型自動判斷對應的是哪個定義。

而在 TS 2.3 版本支持了泛型默認參數(shù),可以某些場景減少函數(shù)類型重載的代碼量,比如對于下面的代碼:

declare function create(): Container;
declare function create(element: T): Container;
declare function create(
  element: T,
  children: U[]
): Container;

通過枚舉表達了范型默認值,以及 U 與 T 之間可能存在的關(guān)系,這些都可以用泛型默認參數(shù)解決:

declare function create(
  element?: T,
  children?: U
): Container;

尤其在 React 使用過程中,如果用泛型默認值定義了 Component

.. Component ..

就可以實現(xiàn)以下等價的效果:

class Component extends React.PureComponent {
  //...
}
// 等價于
class Component extends React.PureComponent {
  //...
}
動態(tài) Import

TS 從 2.4 版本開始支持了動態(tài) Import,同時 Webpack4.0 也支持了這個語法(在 精讀《webpack4.0%20 升級指南》 有詳細介紹),這個語法就正式可以用于生產(chǎn)環(huán)境了:

const zipUtil = await import("./utils/create-zip-file");
準確的說,動態(tài) Import 實現(xiàn)于 webpack 2.1.0-beta.28,最終在 TS 2.4 版本獲得了語法支持。

在 TS 2.9 版本開始,支持了 import() 類型定義:

const zipUtil: typeof import("./utils/create-zip-file") = await import("./utils/create-zip-file")

也就是 typeof 可以作用于 import() 語法,而不真正引入 js 內(nèi)容。不過要注意的是,這個 import("./utils/create-zip-file") 路徑需要可被推導,比如要存在這個 npm 模塊、相對路徑、或者在 tsconfig.json 定義了 paths。

好在 import 語法本身限制了路徑必須是字面量,使得自動推導的成功率非常高,只要是正確的代碼幾乎一定可以推導出來。好吧,所以這也從另一個角度推薦大家放棄 require。

Enum 類型支持字符串

從 Typescript 2.4 開始,支持了枚舉類型使用字符串做為 value:

enum Colors {
  Red = "RED",
  Green = "GREEN",
  Blue = "BLUE"
}

筆者在這提醒一句,這個功能在純前端代碼內(nèi)可能沒有用。因為在 TS 中所有 enum 的地方都建議使用 enum 接收,下面給出例子:

// 正確
{
  type: monaco.languages.types.Folder;
}
// 錯誤
{
  type: 75;
}

不僅是可讀性,enum 對應的數(shù)字可能會改變,直接寫 75 的做法存在風險。

但如果前后端存在交互,前端是不可能發(fā)送 enum 對象的,必須要轉(zhuǎn)化成數(shù)字,這時使用字符串作為 value 會更安全:

enum types {
  Folder = "FOLDER"
}

fetch(`/api?type=${monaco.languages.types.Folder}`);
數(shù)組類型可以明確長度

最典型的是 chart 圖,經(jīng)常是這樣的二維數(shù)組數(shù)據(jù)類型:

[[1, 5.5], [2, 3.7], [3, 2.0], [4, 5.9], [5, 3.9]]

一般我們會這么描述其數(shù)據(jù)結(jié)構(gòu):

const data: string[][] = [[1, 5.5], [2, 3.7], [3, 2.0], [4, 5.9], [5, 3.9]];

在 TS 2.7 版本中,我們可以更精確的描述每一項的類型與數(shù)組總長度:

interface ChartData extends Array {
  0: number;
  1: number;
  length: 2;
}
自動類型推導

自動類型推導有兩種,分別是 typeof:

function foo(x: string | number) {
  if (typeof x === "string") {
    return x; // string
  }
  return x; // number
}

instanceof:

function f1(x: B | C | D) {
  if (x instanceof B) {
    x; // B
  } else if (x instanceof C) {
    x; // C
  } else {
    x; // D
  }
}

在 TS 2.7 版本中,新增了 in 的推導:

interface A {
  a: number;
}
interface B {
  b: string;
}

function foo(x: A | B) {
  if ("a" in x) {
    return x.a;
  }
  return x.b;
}

這個解決了 object 類型的自動推導問題,因為 object 既無法用 keyof 也無法用 instanceof 判定類型,因此找到對象的特征吧,再也不要用 as 了:

// Bad
function foo(x: A | B) {
  // I know it"s A, but i can"t describe it.
  (x as A).keyofA;
}

// Good
function foo(x: A | B) {
  // I know it"s A, because it has property `keyofA`
  if ("keyofA" in x) {
    x.keyofA;
  }
}
4 總結(jié)

Typescript 2.0-2.9 文檔整體讀下來,可以看出還是有較強連貫性的。但我們可能并不習慣一步步學習新語法,因為新語法需要時間消化、同時要連接到以往語法的上下文才能更好理解,所以本文從功能角度,而非版本角度梳理了 TS 的新特性,比較符合學習習慣。

另一個感悟是,我們也許要用追月刊漫畫的思維去學習新語言,特別是 TS 這種正在發(fā)展中,并且迭代速度很快的語言。

5 更多討論
討論地址是:精讀《Typescript2.0 - 2.9》 · Issue #85 · dt-fe/weekly

如果你想?yún)⑴c討論,請點擊這里,每周都有新的主題,周末或周一發(fā)布。

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/95308.html

相關(guān)文章

  • 精讀《Typescript 3.2 新特性》

    摘要:引言發(fā)布了幾個新特性,主要變化是類型檢查更嚴格,對一些時髦功能拓展了類型支持。精讀這次改動意圖非常明顯,是為了跟上的新語法。基本可以算是對社區(qū)的回饋。討論地址是精讀新特性如果你想?yún)⑴c討論,請點擊這里,每周都有新的主題,周末或周一發(fā)布。 1 引言 Typescript 3.2 發(fā)布了幾個新特性,主要變化是類型檢查更嚴格,對 ES6、ES7 一些時髦功能拓展了類型支持。 2 概要 下面挑一...

    cucumber 評論0 收藏0
  • 前端進階資源整理

    摘要:前端進階進階構(gòu)建項目一配置最佳實踐狀態(tài)管理之痛點分析與改良開發(fā)中所謂狀態(tài)淺析從時間旅行的烏托邦,看狀態(tài)管理的設(shè)計誤區(qū)使用更好地處理數(shù)據(jù)愛彼迎房源詳情頁中的性能優(yōu)化從零開始,在中構(gòu)建時間旅行式調(diào)試用輕松管理復雜狀態(tài)如何把業(yè)務(wù)邏輯這個故事講好和 前端進階 webpack webpack進階構(gòu)建項目(一) Webpack 4 配置最佳實踐 react Redux狀態(tài)管理之痛點、分析與...

    BlackMass 評論0 收藏0
  • 精讀《源碼學習》

    摘要:精讀原文介紹了學習源碼的兩個技巧,并利用實例說明了源碼學習過程中可以學到許多周邊知識,都讓我們受益匪淺。討論地址是精讀源碼學習如果你想?yún)⑴c討論,請點擊這里,每周都有新的主題,周末或周一發(fā)布。 1. 引言 javascript-knowledge-reading-source-code 這篇文章介紹了閱讀源碼的重要性,精讀系列也已有八期源碼系列文章,分別是: 精讀《Immer.js》源...

    aboutU 評論0 收藏0
  • 精讀《React 的多態(tài)性》

    摘要:引言本周精讀的文章是,看看作者是如何解釋這個多態(tài)性含義的。讀完文章才發(fā)現(xiàn),文章標題改為的多態(tài)性更妥當,因為整篇文章都在說,而使用場景不局限于。更多討論討論地址是精讀的多態(tài)性如果你想?yún)⑴c討論,請點擊這里,每周都有新的主題,周末或周一發(fā)布。 1 引言 本周精讀的文章是:surprising-polymorphism-in-react-applications,看看作者是如何解釋這個多態(tài)性含...

    tabalt 評論0 收藏0
  • 精讀《用 Reduce 實現(xiàn) Promise 串行執(zhí)行》

    摘要:引言本周精讀的文章是,講了如何利用實現(xiàn)串行執(zhí)行??偨Y(jié)串行隊列一般情況下用的不多,因為串行會阻塞,而用戶交互往往是并行的。更多討論討論地址是精讀用實現(xiàn)串行執(zhí)行如果你想?yún)⑴c討論,請點擊這里,每周都有新的主題,周末或周一發(fā)布。 1 引言 本周精讀的文章是 why-using-reduce-to-sequentially-resolve-promises-works,講了如何利用 reduce...

    JinB 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<