高級類型

交叉類型

把多個類型合并成一個大而全的類型, 運算符為 &

interface obj1 {
  name: string;
  age: number;
}

interface obj2 {
  addr: string,
  fav: string[]
}

type obj = obj1 & obj2
/**
 * {
 *  name: string
 *  age: number
 *  addr: string
 *  fav: string[]
 * }
*/

聯合類型

多個類型的"或"關系, 表示一個值可以是幾種類型之一, 運算符為 |

type UnionType = string | number

const num: UnionType = 123 // OK
const str: UnionType = 'I am string' // OK

類型保護

why: 聯合類型帶了一個問題, 即我們只能訪問此聯合類型的所有類型里共有的成員

interface Bird {
    fly();
    layEggs();
}

interface Fish {
    swim();
    layEggs();
}

function getSmallPet(): Fish | Bird {
    // ...
}

let pet = getSmallPet();
pet.layEggs(); // okay
pet.swim();    // errors

這很好理解, 因為不知道用戶調用時會用到具體哪個類型,所以規定只能使用共同成員.

你可能會說我們可以用 if 條件判斷啊. 是的,可以!

let pet = getSmallPet();

// 每一個成員訪問都會報錯
if (pet.swim) {
    pet.swim();
}
else if (pet.fly) {
    pet.fly();
}

正確姿勢是使用類型斷言:

let pet = getSmallPet();

if ((<Fish>pet).swim) {
    (<Fish>pet).swim();
} else {
    (<Bird>pet).fly();
}

既然有了 if 的類型判斷, else 里面是不是可以省略了? 這就用到類型保護.

自定義類型保護

類型保護就是一些表達式, 它們會在運行時檢查以確保在某個作用域里的類型. 要定義一個類型保護, 我們只要簡單地定義一個函數, 的返回值是一個 類型謂詞:

function isFish(pet: Fish | Bird): pet is Fish {
    return (<Fish>pet).swim !== undefined;
}

在這個例子里, pet is Fish 就是類型謂詞. 謂詞為 parameterName is Type 這種形式, parameterName 必須是來自于當前函數簽名里的一個參數名.

// 'swim' 和 'fly' 調用都沒有問題了

if (isFish(pet)) {
    pet.swim();
} else {
    pet.fly();
}

typeof 類型保護

類型保護雖然香, 但要多寫一個判斷函數太費力了, 況且我們平時用到的聯合類型大多是基本數據類型. 幸運的是, 現在我們不必將 typeof x === "number" 抽象成一個函數, 因為TypeScript 可以將它識別為一個類型保護.

function getLength(value: string | number): number {
  if (typeof value === 'string') return value.length
  else return value.toString().length
}

這些 typeof類型保護只有兩種形式能被識別:

  • typeof v === "typename"
  • typeof v !== "typename"

typename 必須是

  • number
  • string
  • boolean
  • symbol

但是TypeScript 并不會阻止你與其它字符串比較, 語言不會把那些表達式識別為類型保護.

instanceof 類型保護

instanceof 類型保護是通過構造函數來細化類型的一種方式.

interface Padder {
    getPaddingString(): string
}

class SpaceRepeatingPadder implements Padder {
    constructor(private numSpaces: number) { }
    getPaddingString() {
        return Array(this.numSpaces + 1).join(" ");
    }
}

class StringPadder implements Padder {
    constructor(private value: string) { }
    getPaddingString() {
        return this.value;
    }
}

function getRandomPadder() {
    return Math.random() < 0.5 ?
        new SpaceRepeatingPadder(4) :
        new StringPadder("  ");
}

// 類型為SpaceRepeatingPadder | StringPadder
let padder: Padder = getRandomPadder();

if (padder instanceof SpaceRepeatingPadder) {
    padder; // 類型細化為'SpaceRepeatingPadder'
}
if (padder instanceof StringPadder) {
    padder; // 類型細化為'StringPadder'
}

null 類型保護

由于可以為 null 的類型是通過聯合類型實現, 那么你需要使用類型保護來去除 null. 幸運地是這與在JavaScript 里寫的代碼一致:

function f(sn: string | null): string {
    if (sn == null) {
        return "default";
    } else {
        return sn;
    }
}

這里很明顯地去除了 null, 你也可以使用短路運算符:

function f(sn: string | null): string {
    return sn || "default";
}

如果編譯器不能夠去除 nullundefined, 你可以使用類型斷言手動去除. 語法是添加 ! 后綴: identifier!identifier 的類型里去除了 nullundefined:

// ts報錯
function broken(name: string | null): string {
  function postfix(epithet: string) {
    return name.charAt(0) + '.  the ' + epithet; // error, 'name' is possibly null
  }
  name = name || "Bob";
  return postfix("great");
}
// 修改后
function fixed(name: string | null): string {
  function postfix(epithet: string) {
    return name!.charAt(0) + '.  the ' + epithet; // ok
  }
  name = name || "Bob";
  return postfix("great");
}

枚舉成員類型

當每個枚舉成員都是用字面量初始化的時候枚舉成員是具有類型的.

索引類型

通過 索引類型查詢索引訪問操作符

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 索引類型查詢操作符

對于任何類型 T, keyof T 的結果為 T 上已知的公共屬性名的聯合. 例如:

let personProps: keyof Person; // 'name' | 'age'

T[K] 索引訪問操作符

這里, 類型語法反映了表達式語法. 這意味著 person['name'] 具有類型 Person['name'] — 在我們的例子里則為 string類型. 然而, 就像索引類型查詢一樣, 你可以在普通的上下文里使用 T[K], 這正是它的強大所在. 你只要確保類型變量 K extends keyof T 就可以了. 例如下面 getProperty函數的例子:

function getProperty<T, K extends keyof T>(o: T, name: K): T[K] {
    return o[name]; // o[name] is of type T[K]
}

getProperty 里的 o: Tname: K, 意味著 o[name]: T[K]. 當你返回 T[K]的結果, 編譯器會實例化鍵的真實類型, 因此 getProperty 的返回值類型會隨著你需要的屬性改變。

let name: string = getProperty(person, 'name');
let age: number = getProperty(person, 'age');
let unknown = getProperty(person, 'unknown'); // error, 'unknown' is not in 'name' | 'age'

索引類型和字符串索引簽名

keyofT[K] 與字符串索引簽名進行交互. 如果你有一個帶有字符串索引簽名的類型, 那么 keyof T 會是 string . 并且 T[string] 為索引簽名的類型:

interface Map<T> {
    [key: string]: T;
}
let keys: keyof Map<number>; // string
let value: Map<number>['foo']; // number

類型映射

見之前總結的 "內置類型", 是同一個概念

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,643評論 2 380