typeScript-基礎篇(四)

交叉類型和聯合類型

  • 交叉類型比較適合作對象的混入;
  • 聯合類型可使類型具備不肯定性,能夠加強代碼的靈活性
// 交叉類型: 取全部類型的並集
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類型,就說明前面的分支有遺漏,須要補上那個分支

索引類型

  • 索引類型能夠實現對對象屬性的查詢和訪問,
  • 再配合泛型約束,就使咱們 可以創建對象,對象屬性,以及屬性值之間的約束關係
  • 索引類型的查詢操做符
    • keyof T
    • 表示類型 T 的全部公共屬性的字面量聯合類型;
// key的類型是a和b的字面量聯合操做類型
interface Obj {
    a: number;
    b: string;
}
let key: keyof Obj
  • 索引訪問操做符
    • T[K]
    • 表示對象T的屬性k所表明的類型
let value: Obj["a"]
  • 泛型約束
    • T extends U
    • 表示泛型變量能夠經過繼承某個類型或某些屬性
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
  • 剛纔實現的兩個類型Diff 和 NotNull,官方已經爲咱們實現了,分別是
  • Exclude<T, U>
    • 從類型T中過濾掉能夠賦值給U的類型
  • NonNullable<T>
  • Extract<T, U>
    • 從類型T中抽取能夠賦值給U的類型,與Exclude恰好相反
type T6 = Extract<"a", "b", "c", "a" | "e"> // a
  • ReturnType<T>
    • 能夠獲取一個函數返回值的類型 type T7 = ReturnType<() => string> // T7 -> string
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;
相關文章
相關標籤/搜索