// 交叉類型: 取全部類型的並集 interface DogInterface { run(): void } interface CatInterface { jump(): void } let pet:DogInterface & CatInterface = { run(): void jump(): void }
聯合類型數組
// 聯合類型就是聲明的類型並不肯定,能夠爲多個類型中的一個 let a: string | number = "1" // 字面量聯合類型 // 有時候咱們不只要限制變量的類型,還須要限制變量的取值在某個特定的範圍內 // 字符串聯合類型 let c: 'a' | 'b' | 'c' // 數字聯合類型 let b: 1 | 2 | 3 // 對象聯合類型 class Dog implements DogInterface{ run(): void eat(): void } class Cat implements CatInterface{ jump(): void eat(): void } enum Master { Boy, Girl } function getPet(master: Master){ let pet = master === Master.Boy ? new Dog(): new Cat() // 若是一個對象是聯合類型,在類型未肯定的狀況下,只能訪問全部類型的公有成員,這種狀況下只能訪問全部類型的交集 pet.eat() return pet; }
interface Square { kind: "sauare", size: number } interface RectAngle { kind: "rectAngle", width: number, height: number } interface Circle { kind: "circle", r: number } type Shape = Square | RectAngle | Circle function area(s: Shape) { switch (s.kind) { case: sauare: return s.size * s.size case: rectAngle: return s.height * s.width case: circle: return Math.PI * s.r * s.r } } // 沒有創建circle保護區塊,不會報錯 console.log(area({kind: 'circle', r: 2})) // 解決此問題方法1:加返回值: number, // 此時ts會判斷,全部的switch分支是否是包含了全部的狀況; function area(s: Shape): number // 解決此問題方法2: default: return ((e: never) => {throw newError(e)})(s) // 此函數的做用就是: // 檢查s是否是never類型,若是是never類型,說明前面的全部分支都被覆蓋了, 那麼這個分支永遠不會走到; // 若是s不是never類型,就說明前面的分支有遺漏,須要補上那個分支
// key的類型是a和b的字面量聯合操做類型 interface Obj { a: number; b: string; } let key: keyof Obj
let value: Obj["a"]
let obj = { a: 1, b: 2, c: 3 } function getValues(obj: any, keys: string[]){ return keys.map(key => obj[key]) } console.log(getValues(obj, [a, b])) // 1,2 console.log(getValues(obj, [e, f])) // [undefind, undefind] // 此時ts應該報錯 // 通過改造後的函數 function getValues<T,K extends keyof T>(obj: T,keys: K[]): T[K][]{ // 先定義個泛型變量T,用它來約束obj // 再定義一個泛型變量K,用它來約束keys數組 // 把 K 增長一個類型約束,讓他來繼承obj全部屬性的聯合類型,K extends keyof T; // 函數的返回值是一個數組[],數組的類型就是屬性K對應的類型T[K] // 這樣就經過一個索引類型把getValues改造完畢了,這時ts類型檢查就發揮做用了 // 若是咱們指定一個再也不obj範圍內的屬性,ts就會報錯 }
interface Obj { a: string; b: number; c: boolean; } // 好比把接口中的全部屬性變爲只讀; type ReadOnlyObj = Readonly<Obj> // 把接口中的全部屬性變成可選 type PartialObj = Partial<Obj> // 抽取接口中的子集 type PickObj = Pick<Obj,'a' | 'b'> // 同態 // 意思是隻會做用於Object屬性而不會引入新的屬性; // 非同態類型 type RecordObj = Record<'x' | 'y', Obj> // 定義一個類型別名ReadOnlyObj // 接口名稱就是Readonl y // 接口中傳入的類型就是Obj // ts內置類庫Readonly type Readonly<T> = { readonly [P in keyof T]: T[P] } 1. Readonly是一個泛型接口,並且是一個可索引類型的泛型接口 2. 索引簽名是 P in keyof T 3. in keyof T 是一個 索引類型的查詢操做符,表示T全部屬性的聯合類型 4. p in 這裏執行了一次for in 操做;它會把變量P依次綁定到T的全部的屬性上 5. 索引簽名的返回值就是索引操做符了T[P],表明屬性P 所指定的類型了; 6. 最後加上readonly,就把全部的屬性變成了只讀;
T extends U ? X : Y // 若是類型T,能夠被賦值給類型U,結果類型就是X類型,不然Y類型; // 條件類型使類型具備了不惟一性,同時也增長了語言的靈活性; type TypeName<T> = T extends string ? 'string' : T extends number ? 'number' : T extends boolean ? 'boolean' : T extends undefined ? 'undefined' : T extends Function ? 'Function' : "object"; type T1 = TypeName<string> // string type T2 = typeName<string[]> // object
(A | B) extends U ? X : Y // (A extends U ? X : Y) | (B extends U ? X : Y) // 若是T 是一個聯合類型的狀況下,這時候的結果類型就會變成多個條件類型的聯合類型; type T3 = TypeName<string | string[]> // "string" | "object" // T3就會被推斷爲string和object的字面量聯合類型 // 利用這個特性能夠幫咱們實現一些類型的過濾; type Diff<T, U> = T extends U ? never : T type T4 = Diff<"a" | "b" | "c", "a" | "e"> // type T4 = "b" | "c" // Diff會被拆解爲多個條件類型的聯合類型: // Diff<"a", "a" | "e"> | Diff<"b", "a" | "e"> | Diff<"c", "a" | "e"> // never | "b" | "c" // "b" | "c" // 做用:能夠過濾掉類型T中能夠賦值給類型U的類型 // 能夠基於類型 Diff中再作擴展,從類型中除去咱們不須要的類型,好比:null 和 undefind type NotNull<T> = Diff<T, undefind | null > type T5 = NotNull<string | number | undefind | null> type T5 = string | number
type T6 = Extract<"a", "b", "c", "a" | "e"> // a
type RetrunType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any; // 1. RetrunType 要求參數 T 能夠被賦值給一個函數 // 2. 這個函數的參數和返回值都是any; // 3. 因爲這裏函數的返回值是不肯定的,用了一個infer關鍵字 // 4. infer的關鍵字的做用是待推斷或者延遲推斷,須要根據實際狀況來肯定 // 5. 若是實際狀況是R,則返回R,不然返回值類型就是any;