typescript入門:高級類型

學習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
  • type 內的[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[]
複製代碼

索引類型 type[key]

索引到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。 具備三個部分:

  1. 類型變量 K,它會依次綁定到每一個屬性。
  2. 字符串字面量聯合的 Keys,它包含了要迭代的屬性名的集合。
  3. 屬性的結果類型。

咱們能夠對 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 
複製代碼

conditional Type: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運算

上面說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)
複製代碼

conditional type

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,只是憑藉實踐實例得出本身的理解,有誤望指出。

相關文章
相關標籤/搜索