交叉類型(Intersection Types)
-
某個類型的對象同時擁有多種類型的成員,交叉類型使用
&
創建function extend<T, U>(first: T, second: U): T & U { let result = <T & U>{}; for (let id in first) { (<any>result)[id] = (<any>first)[id]; } for (let id in second) { if (!result.hasOwnProperty(id)) { (<any>result)[id] = (<any>second)[id]; } } return result; } class Person { constructor(public name: string) { } } interface Loggable { log(): void; } class ConsoleLogger implements Loggable { log() { // ... } } var jim = extend(new Person("Jim"), new ConsoleLogger()); var n = jim.name; jim.log();
聯合類型(Union Types)
與交叉類型對應,聯合類型表示一個值可以是幾種類型之一,用
|
表示-
如果一個值是聯合類型,我們只能訪問此聯合類型的所有類型里共有的成員
interface Bird { fly(); layEggs(); } interface Fish { swim(); layEggs(); } function getSmallPet(): Fish | Bird { // ... } let pet = getSmallPet(); pet.layEggs(); // okay pet.swim(); // errors
類型保護
用戶自定義類型保護
-
由于聯合類型中,我們只能訪問所有類型共有的成員,想要確切的判斷是否為某個類型只能反復使用斷言
let pet = getSmallPet(); if ((<Fish>pet).swim) { (<Fish>pet).swim(); } else { (<Bird>pet).fly(); }
可以通過定義類型保護避免反復斷言
function isFish(pet: Fish | Bird): pet is Fish { return (<Fish>pet).swim !== undefined; } // 返回值pet is Fish 是一個類型謂詞 // pet為函數參數,Fish是聯合類型的成員之一 // 調用一次類型保護后,TypeScript會將變量縮減為那個具體的類型 if (isFish(pet)) { pet.swim(); } else { pet.fly(); }
typeof
類型保護(基礎類型保護)
- 只有兩種形式
typeof v === "typename"
和typeof v !== "typename"
-
"typename"
必須是"number"
,"string"
,"boolean"
或"symbol"
,與其它字符串比較不會被識別為類型保護
instanceof
類型保護
- 通過構造函數來細化類型
-
instanceof
的右側要求是一個構造函數,TypeScript將細化為:- 此構造函數的
prototype
屬性的類型,如果它的類型不為any
的話 - 構造簽名所返回的類型的聯合
- 此構造函數的
可以為null的類型
默認情況下,類型檢查器認為
null
與undefined
可以賦值給任何類型。null
與undefined
是所有其它類型的一個有效值;--strictNullChecks
標記可以解決此錯誤當使用了
--strictNullChecks
,可選參數和屬性會被自動地加上| undefined
-
可以通過js的寫法使用類型保護去除null
function f(sn: string | null): string { if (sn == null) { return "default"; } else { return sn; } } function f(sn: string | null): string { return sn || "default"; }
或者通過斷言的形式,寫法是添加
!
后綴function fixed(name: string | null): string { function postfix(epithet: string) { return name!.charAt(0) + '. the ' + epithet; // ok } name = name || "Bob"; return postfix("great"); }
類型別名(type)
通過
type
關鍵字取別名-
別名不會新建一個類型,只是已有類型的引用
type Name = string; type NameResolver = () => string; type NameOrResolver = Name | NameResolver; function getName(n: NameOrResolver): Name { if (typeof n === 'string') { return n; } else { return n(); } }
-
類型別名也可以是泛型
type Tree<T> = { value: T; left: Tree<T>; right: Tree<T>; }
-
類型別名和接口的區別
-
接口可以在任何地方使用,而類型別名不能出現在聲明右側的任何地方
type Yikes = Array<Yikes>; // error
接口創建了一個新名字,而類型別名不會
類型別名不能被
extends
和implements
(自己也不能extends
和implements
其它類型)當無法通過接口來描述一個類型,并且需要用到聯合類型或元組類型時會使用別名
-
字符串字面量類型
-
字符串字面量可以實現類似枚舉的效果
// 聯合類型 // 字符串字面量類型 type Easing = "ease-in" | "ease-out" | "ease-in-out"; class UIElement { animate(dx: number, dy: number, easing: Easing) { if (easing === "ease-in") { // 類型保護 // ... } else if (easing === "ease-out") { } else if (easing === "ease-in-out") { } else { // error! should not pass null or undefined. } } }
-
字符串字面量可用于區分函數重載
function createElement(tagName: "img"): HTMLImageElement; function createElement(tagName: "input"): HTMLInputElement; // ... more overloads ... function createElement(tagName: string): Element { // ... code goes here ... }
數字字面量類型
-
感覺沒啥特別用處
function rollDie(): 1 | 2 | 3 | 4 | 5 | 6 { // ... }
枚舉成員類型
-
當每個枚舉成員都是用字面量初始化的時候枚舉成員是具有類型的,即枚舉成員類型
enum ShapeKind { Circle, Square, }
可辨識聯合(Discriminated Unions)
-
可辨識聯合的三個要素
- 具有普通的單例類型屬性— 可辨識的特征(即都有一個同樣的屬性用于分辨聯合的成員)
- 一個類型別名包含了那些類型的聯合— 聯合(即用類型別名來組織聯合)
- 此屬性上的類型保護
interface Square { kind: "square"; // 要素一 可辨識特征 size: number; } interface Rectangle { kind: "rectangle"; width: number; height: number; } interface Circle { kind: "circle"; radius: number; } // 要素二 用類型別名聯合 type Shape = Square | Rectangle | Circle; function area(s: Shape) { switch (s.kind) { // 要素三 可辨識特征的類型保護 case "square": return s.size * s.size; case "rectangle": return s.height * s.width; case "circle": return Math.PI * s.radius ** 2; } }
多態的 this
類型
-
通過返回this實現鏈式調用
class BasicCalculator { public constructor(protected value: number = 0) { } public currentValue(): number { return this.value; } public add(operand: number): this { this.value += operand; return this; } public multiply(operand: number): this { this.value *= operand; return this; } // ... other operations go here ... } class ScientificCalculator extends BasicCalculator { public constructor(value = 0) { super(value); } public sin() { this.value = Math.sin(this.value); return this; } // ... other operations go here ... } let v = new ScientificCalculator(2) .multiply(5) .sin() .add(1) .currentValue();
索引類型(Index types)
function pluck<T, K extends keyof T>(o: T, names: K[]): T[K][] {
return names.map(n => o[n]);
}
interface Person {
name: string;
age: number;
}
let person: Person = {
name: 'Jarid',
age: 35
};
let strings: string[] = pluck(person, ['name']); // ok, string[]
索引類型查詢操作符
keyof T
-
keyof T
等同于T
上已知的公共屬性名的聯合,如keyof Person
完全可以與'name' | 'age'
替換
索引訪問操作符
-
T[K]
類似于對象索引
映射類型
-
通過轉化的方式從舊類型中創建新類型,如
interface PersonPartial { name: string; age: number; } // 統一轉化為只讀 type Readonly<T> = { readonly [P in keyof T]: T[P]; } // 統一轉化為可選 type Partial<T> = { [P in keyof T]?: T[P]; }
-
typescript還內置了一些映射類型,可以直接使用,如
type Pick<T, K extends keyof T> = { [P in K]: T[P]; } type Record<K extends string, T> = { [P in K]: T; } // 預定義的有條件類型 Exclude<T, U> -- 從T中剔除可以賦值給U的類型。 Extract<T, U> -- 提取T中可以賦值給U的類型。 NonNullable<T> -- 從T中剔除null和undefined。 ReturnType<T> -- 獲取函數返回值類型。 InstanceType<T> -- 獲取構造函數類型的實例類型