轉載至微信公衆號:方凳雅集javascript
前言:在編寫 typescript 應用的時候,有時候咱們會但願複用或者構造一些特定結構的類型,這些類型只從 typescript 靠內建類型和 interface、class 比較難以表達,這時候咱們就須要用到類型推導。java
在 typescript 咱們能夠用 keyof 關鍵字來提取對象的索引標記.es6
// obj 是一個對象, typeof 獲得了其類型
keyof (typeof obj)
複製代碼
對於 es5 而言,毋庸置疑一個對象(hash dictionary)的索引只多是 string 和 number 兩種類型;typescript
// One simiple object with any type key-value
interface Foo {
[k: string]: any
}
type TFOO = keyof Foo // string | number
複製代碼
數組的元素索引有本身特殊含義,但它的類型仍然是 number.數組
const a = []
type TA = keyof (typeof a)
/**
number | "length" | "toString" | "toLocaleString" | "pop" | "push" | "concat" | "join" | "reverse" | "shift" | "slice" | "sort" | "splice" | "unshift" | "indexOf" | "lastIndexOf" | ...
*/
複製代碼
從 es6 開始, javascript 容許使用 Symbol 做爲對象索引bash
interface objWithSymbol {
foo: string
[Symbol.toStringTag]: any
}
type T_OBJ_WITH_SYM = keyof objWithSymbol // "foo"
const a_with_symkey = {
[Symbol('#symA')]: 'bar'
}
type T_A = keyof typeof a_with_symkey // number | string
複製代碼
截止到筆者書寫到此爲止, typescript 還不支持經過 keyof 關鍵詞提取 Symbol 類型的索引,這也無可厚非,由於在表述上,每每 Symbol 類型的索引並不被當成 "key". 也沒有別的官方方式能夠直接提取一個 interface 或對象類型中的 Symbol 類型的索引. 這其實能夠理解: Symbol 做爲對象索引的意義在於惟一性, 它自己不具備字面量(literal text),其惟一性的保障是運行時的內存分配,而非字面量.微信
在 typescript 中,這樣寫會被提示違反了類型約束:app
const a = {}
// lint: 類型「{}」上不存在屬性「foo」。ts(2339)
a.foo = 'bar'
複製代碼
但這樣就不會:ui
const a = {}
a[Symbol('#symA')] = 'bar'
複製代碼
注意 Symbol 做爲對象的索引是, 其不具備 enumerable: true 的屬性,即默認沒法被 Object.keys(...)
和 for...in
提取.
相似於獲取獲取一個對象中 string | number 類型的索引的方法是 Object.getOwnPropertyNames(); 獲取一個對象中全部 Symbol 類型索引的方式 Object.getOwnPropertySymbols();es5
有時候咱們聲明瞭一個全部元素類型一致(好比都爲 string)的數組(類型爲 string[]), 咱們但願獲得數組中的元素的類型, 用於後續的變量約束,這時候怎麼辦?
const a: string[] = []
type ELE_A = string
複製代碼
對簡單的內建類型,咱們固然能夠簡單聲明,或者乾脆就把 a 聲明爲 ELE_A[]
;
若是是這樣呢?
const a: {a: string, b: string, c: number}[] = []
複製代碼
咱們固然能夠提早聲明 ELE_A , 而後把 a 聲明爲 ELE_A[]
那若是是這樣呢?
const a: {a: string, b: string, c: number}[] = []
const a1: {a: string, b: string}[] = []
const a2: {foo: string, c: string}[] = []
複製代碼
若是對每一個變量都提早聲明,不免讓人有種在寫 C 的感受:先聲明、再調用。
使用 any 能夠幫咱們提取其中的元素,好比
const a: {a: string, b: string, c: number}[] = []
type T_A = (typeof a)[any]
const a1: {a: string, b: string}[] = []
type T_A1 = (typeof a1)[any]
const a2: {foo: string, c: string}[] = []
type T_A2 = (typeof a2)[any]
複製代碼
這樣,對於只複用一兩次的數組元素中的類型,咱們沒必要特地提早聲明,而是先聲明變量,再提取.
對於如下 interface, 若是想提取 foo2 (是一個數組)中的元素的類型,怎麼辦?
interface A {
foo: {
foo2: {
foo3: string[]
}[]
}
}
複製代碼
直接從 A 索引到 foo2, 而後使用 any 提取其元素
type FOO2_ELE = A['foo']['foo2'][any]複製代碼