TypeScript全解:聯(lián)合類型

JS 可以對(duì)值進(jìn)行加減運(yùn)算
如果把 TS 的類型系統(tǒng)的當(dāng)作一門語(yǔ)言,
TS 可以對(duì)類型進(jìn)行各種運(yùn)算嗎?
如果有,那么 TS 類型系統(tǒng)有那些運(yùn)算呢?

聯(lián)合類型(union type)(也叫做并集)

栗子??

type A1 = number;
type B1 = string;
type C1 = A1 | B1;
const c1: C1 = 42;

上述代碼用圖表示即為:

type A2 = { name: string };
type B2 = { age: number };
type C2 = A2 | B2;
const c2: C2 = {
  name: 'John',
  age: 18,
}

如何使用?

const f1 = (a: number | string) => {
  a.xxx?
  // 這里能使用出 a 的那些方法?
  // 即不能把 a 當(dāng)作 number
  // 也不能把 a 當(dāng)作 string
  // 那么,怎么使用 a 變量呢?
}

想辦法把類型取分開來即可:

const f1 = (a: number | string) => {
  if (typeof a === 'number') {
    a.toFixed(2) // 這里可以調(diào)用 Number 的方法
  } else {
    a.split('') // 這里可以調(diào)用 String 的方法
  }
}

這樣一來,即可保證使用的安全,這種行為也叫類型收窄。

用JS做類型收窄

typeof

js 的 typeof 有以下幾種返回值:

  • string
  • number
  • bigint
  • boolean
  • symbol
  • undefined
  • object
  • function

我們發(fā)現(xiàn) tpyeof 的有以下的局限性:數(shù)組對(duì)象、普通對(duì)象、日期對(duì)象、null 都無法區(qū)分

const a = (params: Date | Date[]) => {
  if (typeof params === 'object') {
    params // 此時(shí)類型仍然為 Date | Date[]
  }
}

但是我們還能使用 instanceof

instanceof

這個(gè)方法主要返回對(duì)象的類

const a = (params: Date | Date[]) => {
  if (params instanceof Date) {
    params // 此時(shí)類型收窄為 Date
  } else if (params instanceof Array) {
    params // 此時(shí)類型收窄為 Array
  }
}

instanceof 的局限性:不支持 TS 獨(dú)有的類型

type Person = {
  age: number;
}
type Animal = {
  name: string;
}
const a = (params: Person | Animal) => {
  if (params instanceof Person) {  // TODO: 報(bào)錯(cuò),Person 是一個(gè)類型,而不是類   
    // ...
  }
}

那么如何解決呢?

in

type Person = {
  age: number;
}
type Animal = {
  name: string;
}
const a = (params: Person | Animal) => {
  if ('name' in params) {  
    params // 這里的類型為 Animal
  } else if ('age' in params) {
    params // 這里的類型為 Person
  }
}

in 的局限性:只能區(qū)分部分對(duì)象,比如沒有相同屬性的對(duì)象、日期對(duì)象、函數(shù)

思考,怎么有這么多的局限性

因?yàn)槲覀円恢痹谟?js 的邏輯來判斷 ts 里面的邏輯,這是兩門語(yǔ)言,根本無法做到一一對(duì)應(yīng)

更多收窄,見這份文檔

眾所周知:js 的類型系統(tǒng)就是辣雞,那么有沒有取分類型的萬全之法

類型謂詞/類型判斷

is

type Person = {
  age: number;
  sex: number;
}
type Animal = {
  name: string;
  legs: string[];
}

const a = (params: Perosn | Animal) => {
  if (isPerson(a)) {
    params // 這里的類型一定為 Person  
  }
}

function isPerson(x: Person | Animal /* 這里可以寫 any */): x is Person {
  // 這里可以使用上面所有的類型判斷技巧
  return 'age' in x && 'sex' in x // 必須返回 boolean
}
const isAnimal = (x: any): x is Animal => 'name' in x && 'legs' in x && a instanceof Array

有點(diǎn)是支持所有 TS 類型,但是隨之而來的缺點(diǎn)就是 麻煩!那么有沒有更簡(jiǎn)單的方法呢?

可辨別聯(lián)合

直接上代碼

type Person = { kind: 'person'; name: string; age: number; }
type Animal = { kind: 'animal'; name: string; }

const a = (params: Person | Animal) => {
  if (params.kind === 'person') {
    params // 這里的類型為 Person 
  } elseif (params.kind === 'animal') {
    params // 這里的類型為 Animal
  }
}
  • 優(yōu)點(diǎn):把復(fù)雜類型的收窄變成簡(jiǎn)單類型的對(duì)比
  • 局限性:1. 必須加上相同屬性 kind; 2. kind 的類型必須是簡(jiǎn)單類型; 3. 各類型中的 kind 無交集

則稱為可辨別聯(lián)合

斷言

type Person = {
  age: number;
}
type Animal = {
  name: string;
}
const a = (params: Person | Animal) => {
  (params as Person).age // 強(qiáng)制告訴 ts 此時(shí)類型為 Person
}

思考:any 是否等于所有類型(除 never/unknown/any/void)的聯(lián)合?為什么?

答案是不是

之前在基礎(chǔ)類型中介紹過,并畫過圖,any 包含了所有的類型,但是僅僅限于那個(gè)時(shí)候,在了解過類型收窄以后,就不能這么理解了。

怎么證明?用反證法:

正常來說一旦出現(xiàn)聯(lián)合類型,那么這個(gè)時(shí)候你的類型就不能再使用了

const a = (params: number | string) => {
  a // 此時(shí)這里只能調(diào)用 number 和 string 的共用方法,無法調(diào)用比如 split 這種字符串特有方法
}

但是如果是 any,則可以繼續(xù)使用

const a = (params: any) => {
  a.split() // 不會(huì)報(bào)錯(cuò)
  a.toFixed() // 不會(huì)報(bào)錯(cuò)
}

這個(gè)時(shí)候我們?cè)賮砜纯?a target="_blank">官方文檔

解釋為這是一個(gè)特殊的類型,如果你不想讓你的類型報(bào)錯(cuò)你可以使用這個(gè)類型

其實(shí)文檔也沒有說的很清楚,目前來看 TS 絕大部分規(guī)則對(duì) any 不生效

const a = (params: any) => {
  const newParams: never = params // TODO: 報(bào)錯(cuò)
}

那么什么類型等于所有類型(除 never/unknown/any/void)的聯(lián)合呢?為什么?

答案就是 unknown

const a = (params: unknown) => {
  if (typeof === 'number') {
    params.toFixed() // 這里的類型是 number
  } elseif (params instanceof Data) {
    params // 這里的類型是 Date
  }
}

也就是說 unknown 可以收窄為任意類型,那么反過來說就是 unknown 就是所有類型的聯(lián)合,必須通過收窄后才能使用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容