學習typescript中 ,有一個小夥伴提出了一個問題typescript
const a = {
a:'1',
b:'2',
c:'3'
}
複製代碼
如何取到每一個鍵上的值 ,組成一個聯合類型 ? 即獲得一個類型爲編程
type forA = "1" | "2" | "3"
複製代碼
一位大神給出了答案數組
const a = {
a:'1',
b:'2',
c:'3'
} as const
type t<T> = T[keyof T]
type forA = t<typeof a> //"1" | "2" | "3"
複製代碼
我已經開始迷迷糊糊了。接着提問者又延伸了這個問題markdown
const a = {
a:'1',
b:'2',
c:'3',
d:{
f:'4'
}
}
複製代碼
如何取到每一個鍵上的值 (包括一個深層嵌套),組成一個聯合類型 ? 即獲得一個類型爲ide
type forA = "1" | "2" | "3" | "4"
複製代碼
過了一會,大神又拿出了回答函數
const a = {
a:'1',
b:'2',
c:'3',
d:{
f:'4'
}
} as const
type t<T> = {
[K in keyof T]:T extends object
? t<T[K]>
: T[K]
}[keyof T]
type forA = t<typeof a> //"1" | "2" | "3" | "4"
複製代碼
什麼?類型裏面還有遞歸!對知識查漏補缺,列下如下幾點oop
as const
[K in keyof T]
extends A ? B : C
類型的三目表達式?[keyof T]
keyof 和 typeof
keyof
得到接口或者type alias 的鍵學習
interface interfaceNumber{
one:string,
two:number
}
type typeNumber = {
three:string,
four:number
}
type interfaceKey = keyof interfaceNumber // "one" | "four"
type numberKey = keyof typeNumber // "three" | "four"
複製代碼
typeof
使一個上文的某個 Javascript變量 轉換爲typescript中的類型ui
const someType = {
obj:{
one:1
},
arr:[1,2,4],
num:1
}
type t = typeof someType
//{
// obj: {
// one: number;
// };
// arr: number[];
// num: number;
//}
複製代碼
類型系統遞歸地把一個對象上鍵的值轉換爲對應類型spa
as const
把一個數字類型或者字符串類型在轉換爲類型時,縮緊爲字面量。
const a = 1 as const;
type a1 = typeof a;// 1
const b = "i am b" as const;
type b1 = typeof b;// "i am b"
const c = [1,"23434" , 1] as const
type c1 = typeof noArray // readonly [1, "23434", 1]
複製代碼
如何理解字面量和string
類型?字面量也是單獨的一個類型,而類型string
,能夠理解爲無窮(全部)字面量的聯合 "a" | "b" | "c"|....
它是字面量的全集。同理number
as const
還能夠靈活穿插在對象中使用 , 可是數組不行。
const someType = {
obj:{
one:1 as const
},
num:1
}
//typeof someType
//{
// obj: {
// one: 1;
// };
// num: number;
//}
const Array = [1,2 as const , 1]
//typeof Array
//number[]
複製代碼
索引到interface或者tpye alias
對應鍵的類型
const someType = {
obj:{
one:1
},
arr:[1,2,4],
num:1
}
type t = typeof someType
type obj = t["obj"]//{ one:number }
interface T{
a:string,
t:{
p:number
}
}
type a = T["a"]//string
type p = T["t"]["p"]//number
複製代碼
使用 t.obj
來索引類型是無效的 ,命名空間才能夠這麼作。
若是索引聯合字符串,則結果也是被索引的鍵的類型的聯合。
interface T{
a:string,
b:number
}
type t1 = T["a" | "b"]// string | number
//或者
type t2 = T[ keyof T ]// string | number
複製代碼
[ K in keyof T]
只能在type
中用。
type getType1<T>= {
[K in keyof T]: T[K]
}
//報錯
//計算屬性名的類型必須爲 "string"、"number"、"symbol" 或 "any"。ts(2464)
//成員「[K in keyof」隱式包含類型「any」。ts(7008)
interface getType2<T>{
[K in keyof T]: T[K]
}
複製代碼
可做爲固定的語法 [K in keyof T]
,K表示全部 T 中的鍵被拆分紅一個個鍵,後面能夠聲明類型
const someType = {
obj:{
one:1
},
num:2
}
type getType1<T>= {
[K in keyof T]: T[K]
}
type instance1 = getType1<typeof someType>
//{
// obj:{
// one:string
// }
// num:number
//}
複製代碼
keyof T
結果是一組key的聯合,下面原始的語法也是可行的
type Keys = "option1" | "option2";
type Flags = { [K in Keys]: boolean };
複製代碼
你已經發現。映射類型如同JavaScript
中的 for(let K in T) 。如下是官方說明
它的語法與索引簽名的語法類型,內部使用了
for .. in
。 具備三個部分:
- 類型變量
K
,它會依次綁定到每一個屬性。- 字符串字面量聯合的
Keys
,它包含了要迭代的屬性名的集合。- 屬性的結果類型。
咱們能夠對 T[K]
作一些處理。
const someType = {
obj:{
one:1
},
num:2
}
//extends 相似於三目運算符
type getType1<T>= {
[K in keyof T]: T[K] extends object ? 1 : 0
}
//object類型轉換爲字面量1,其餘類型爲0
type instance1 = getType1<typeof someType>//{obj: 1;num: 0;}
複製代碼
extends後面會說起,此處能夠簡單做爲三目表達式。
type或者interface,能夠看做一個函數,它能夠在{}內部調用自身。
const a {
a:'1',
b:'2',
c:{
d:5
}
} as const
type t<T> = { [ K in typeof T]:T[K] extends object ? t<T[K]> : T[K] }
type forA = t<type of a> //"1" | "2" | 5
複製代碼
若是沒有泛型參數,它也是泛型函數,也能夠調用自身
//這並不報錯
interface Map {
[key: string]: Map;
}
複製代碼
若是沒有{},則不能夠調用自身
//Add是把const類型相加的泛型函數
//報錯:類型別名「F」循環引用自身。ts(2456)
type F<T extends number> = T extends 10 ? F<Operate.Add<T,1>> : 0
複製代碼
T extends U ? X : Y
extends
關鍵字首先說明,typescript多處使用 extends
關鍵字,但它們的做用不盡相同。
//js中的類繼承,與ts無關
class A extends B{}
//接口繼承
interface Shape {
color: string;
}
interface Square extends Shape {
sideLength: number;
}
//泛型約束
//泛型參數T必須有用length鍵且類型爲number
type getType1<T extends {length:number}>= {
[K in keyof T]: T[K]
}
//約束K必須包括一個或以上T的鍵值
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
}
//conditional type
type F<T> = T extends object ? 0 : 1
複製代碼
typescript中的extends
三種場景,
泛型約束,在你輸入被約束的泛型參數時,強迫輸入值與extends
的運算爲true
conditional type
,它的運算可真可假 ,true的結果返回X,false返回Y
接口繼承,若是interface A extends B{}
, 那麼A extends B
是必定爲真的
以個人理解,它們含義上是一脈繼承的。
上面說extends
的含義上有關聯,可是實際conditional type 和 泛型約束在邊緣狀況下存在差異。
泛型約束的extends
結果要麼是true
要麼是 false
,這個是明確的,它決定編譯器的報錯與否。
假設 type T<A extends B>
,如A或B有一邊是聯合類型時,它遵循着一個規則,A必須是B子集
具體而言就是,A的每一個元素extends
B都要爲真。
type B = "1" | "2"
type T<A extends B>= {
[key in A] :0
}
T<"1"> // { "1":0 }
T<"1" | "2"> //{"1":0,"2":0}
T<"1" | "2" | "3">//報錯 不能將類型「"3"」分配給類型「"2" | "1"」。ts(2344)
//至關於
//("1" extends B)&("2" extends B)&("3" extends B)
複製代碼
distributive conditional types
conditional types中,若是T是 naked type那麼 conditional types就被稱爲分配條件類型(distributive conditional types)。
naked type,然而Typescript並無說明啥是naked type, 咱們大體能夠認爲就是這個type沒有被包裹在其餘的複合結構裏,如 array , record , function等。如咱們能夠經過將T包裹爲[T]來破壞naked type
type F<T> = T extends U ? X : Y
type union_type = A | B | C
type a = F<union_type>
//那麼a的結果爲 A extends U ? X :Y | B extends U ? X :Y | C extends U ? X : Y
複製代碼
有一點相似泛型約束的分配運算
其餘更加特殊的狀況,能夠看這篇文章:深刻typescript類型系統(二): 泛型和類型元編程
最後 咱們再看看文章開頭的答案
type t<T> = {
[K in keyof T]:T extends object
? t<T[K]>
: T[K]
}[keyof T]
複製代碼
已經能夠分析清楚了~
剛剛入門typescript,只是憑藉實踐實例得出本身的理解,有誤望指出。