TypeScript學(xué)習(xí)-Advanced Types


Intersection Types

它是把多個(gè)類(lèi)型組合成一個(gè)類(lèi)型,它使得你使用這個(gè)類(lèi)型的時(shí)候享有定義的所有類(lèi)型的特征漂羊。
例如:

Person & Serializable & Loggable

即你定義的具有這個(gè)類(lèi)型的對(duì)象擁有了這三種類(lèi)型包括的所有成員帐偎。

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

Intersection Types類(lèi)似于集合運(yùn)算里面的并和差的關(guān)系。
Union Types代表多選一。使用(|)顿乒,來(lái)分隔類(lèi)型。number | string | boolean

function padLeft(value: string, padding: string | number) {
    // ...
}
let indentedString = padLeft("Hello world", true); // errors during compilation

當(dāng)一個(gè)值是Union Types泽谨,我們只能使用所有類(lèi)型里面公有的屬性璧榄,因?yàn)槠渌膶傩圆淮_實(shí)是否有,所以使用吧雹,TypeScript會(huì)報(bào)錯(cuò)骨杂。

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

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

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

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

Type Guards and Differentiating Types

Union types對(duì)于建模情形是非常有用的,這種情形下雄卷,他們所帶的類(lèi)型的中有重疊的值搓蚪。
例如:我們想知道在什么情況下是某一特定類(lèi)型,這樣我們就能有條件的使用它丁鹉。
在一般的JavaScript中妒潭,我們判斷是否存在特定的成員來(lái)判斷。

let pet = getSmallPet();
// Each of these property accesses will cause an error
if (pet.swim) {
    pet.swim();
}
else if (pet.fly) {
    pet.fly();
}

然而在TypeScript中只允許訪問(wèn)類(lèi)型共有的值揣钦。
所以我們可以使用type assertion

let pet = getSmallPet();
if ((<Fish>pet).swim) {
    (<Fish>pet).swim();
}
else {
    (<Bird>pet).fly();
}
User-Defined Type Guards

因?yàn)樯厦嫖覀冇昧硕啻?code>type assertion雳灾,所以我們需要改進(jìn)。只做一次判斷冯凹,就知道接下來(lái)的分支里面是什么類(lèi)型谎亩。
所以TypeScript引入了 type guard
在一個(gè)函數(shù)的返回類(lèi)型加上parameterName is Type

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

如果變量的類(lèi)型和源類(lèi)型兼容,也就是確實(shí)是 Fish | Bird宇姚,那么TypeScript會(huì)把變量的類(lèi)型縮小到特定的類(lèi)型匈庭,也就是我們的斷言類(lèi)型。

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

這樣TypeScript既知道if分支里面是Fish空凸,也知道else分支里面是Bird

typeof type guards

當(dāng)我們需要做分支判斷的時(shí)候嚎花,引入上面的padLeft例子。

function isNumber(x: any): x is number {
    return typeof x === "number";
}
function isString(x: any): x is string {
    return typeof x === "string";
}
function padLeft(value: string, padding: string | number) {
    if (isNumber(padding)) {
        return Array(padding + 1).join(" ") + value;
    }
    if (isString(padding)) {
        return padding + value;
    }
    throw new Error(`Expected string or number, got '${padding}'.`);
}

上面需要自己定義額外的函數(shù)來(lái)判斷原始類(lèi)型呀洲,這樣是很痛苦的紊选;但是這樣的判斷很常用啼止,所以TypeScript提供了typeof guard type,來(lái)幫助減少工作兵罢。

function padLeft(value: string, padding: string | number) {
    if (typeof padding === "number") {
        return Array(padding + 1).join(" ") + value;
    }
    if (typeof padding === "string") {
        return padding + value;
    }
    throw new Error(`Expected string or number, got '${padding}'.`);
}

現(xiàn)在你只需要寫(xiě)成上面的樣子就可以了献烦。
typeof guard type是如下兩種格式:

  • typeof x === "number"
  • typeof x !== "number"

其中屬于typeof guard type原始類(lèi)型只包括:number, string, boolean, symbol,你也可以自己定義比較其他的類(lèi)型卖词,但是不會(huì)被認(rèn)為是typeof guard type

instanceof type guards

格式如下:
instance instanceof constructor
當(dāng)使用這個(gè)保衛(wèi)類(lèi)型的時(shí)候巩那,TypeScript會(huì)將變量的類(lèi)型按順序縮小到一下兩個(gè)范圍:

  • 如果這個(gè)構(gòu)造函數(shù)的原型的類(lèi)型不是any,就會(huì)縮小到這個(gè)原型類(lèi)型
  • 否則返回這個(gè)構(gòu)造函數(shù)簽名的union types類(lèi)型
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("  ");
}
// Type is 'SpaceRepeatingPadder | StringPadder'
let padder: Padder = getRandomPadder();
if (padder instanceof SpaceRepeatingPadder) {
    padder; // type narrowed to 'SpaceRepeatingPadder'
}
if (padder instanceof StringPadder) {
    padder; // type narrowed to 'StringPadder'
}

Nullable types

TypeScript有兩個(gè)特殊類(lèi)型此蜈。nullundefined即横,他們?cè)跊](méi)有限制的條件下能夠賦值給任何類(lèi)型。
如果加上--strictNullChecks選項(xiàng)裆赵,只能賦值給各自的類(lèi)型和void

Type guards and type assertions

因?yàn)榭梢詾榭盏念?lèi)型(nullable)是union type东囚,所以你需要排除null,方法和JavaScript中一樣。

  • 方法一
function f(sn: string | null): string {
    if (sn == null) {
        return "default";
    }
    else {
        return sn;
    }
}
  • 方法二
function f(sn: string | null): string {
    return sn || "default";
}
  • 方法三战授,如果編譯器不能排除null页藻,你就需要用type assertions來(lái)手動(dòng)排除。
    例如在嵌套的函數(shù)中植兰,編譯器不能追蹤到所有調(diào)用(除非這個(gè)嵌套函數(shù)是立即執(zhí)行的(IIFE))份帐,尤其是這個(gè)嵌套函數(shù)作為返回的結(jié)果返回給外面的函數(shù)時(shí)。
    不知道在哪里會(huì)調(diào)用這個(gè)嵌套函數(shù)楣导,所以在函數(shù)體執(zhí)行時(shí)废境,也就不不知道參數(shù)的類(lèi)型,所以編譯器無(wú)法排除null爷辙。
    手動(dòng)排除方法:通過(guò)identifier! 排除null
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");
}

其實(shí)綜上彬坏,就是非立即執(zhí)行的嵌套函數(shù)在調(diào)用時(shí)朦促,由于它存在的那個(gè)函數(shù)里已經(jīng)執(zhí)行完畢膝晾,所以它引用的那個(gè)函數(shù)中的變量類(lèi)型以及不可知了。


Type Aliases

它能給類(lèi)型創(chuàng)建一個(gè)名字务冕。Aliases不會(huì)創(chuàng)建新類(lèi)型血当,只會(huì)創(chuàng)建一個(gè)新名字來(lái)引用這個(gè)類(lèi)型。
給原始類(lèi)型創(chuàng)建別名不是很有用禀忆,盡管它能用于文檔形式臊旭。

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 aliases也可以是一個(gè)泛型。

type Container<T> = { value: T };

我們能在type alias的屬性中引用自己箩退。構(gòu)建樹(shù)形類(lèi)型离熏。

type Tree<T> = {
    value: T;
    left: Tree<T>;
    right: Tree<T>;
}

intersection types組合出一些非常高級(jí)的類(lèi)型。

type LinkedList<T> = T & { next: LinkedList<T> };

interface Person {
    name: string;
}

var people: LinkedList<Person>;
var s = people.name;
var s = people.next.name;
var s = people.next.next.name;
var s = people.next.next.next.name;

但是戴涝,我們不能在別名申明語(yǔ)句等號(hào)右邊使用這個(gè)別名滋戳。

type Yikes = Array<Yikes>; // error
Interfaces vs Type Aliases
  • 接口創(chuàng)建了一個(gè)新名字钻蔑,而類(lèi)型別名只是返回一個(gè)對(duì)象字面量
  • 接口可繼承擴(kuò)展,或被實(shí)現(xiàn)奸鸯。類(lèi)型別名不能咪笑。
    因?yàn)橐粋€(gè)理想的軟件是可擴(kuò)展的。所以我們盡可能使用Interface娄涩,如果在遇到Interface表達(dá)不了的shape時(shí)窗怒,或者需要使用union typestuple type的時(shí)候,才需要使用類(lèi)型別名蓄拣。

String literal type

  • union,guard type,alias一起使用來(lái)獲得一個(gè)類(lèi)枚舉的string類(lèi)型扬虚。
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.
        }
    }
}
let button = new UIElement();
button.animate(0, 0, "ease-in");
button.animate(0, 0, "uneasy"); // error: "uneasy" is not allowed here
  • 也用來(lái)區(qū)分真正函數(shù)和重載函數(shù)
function createElement(tagName: "img"): HTMLImageElement;
function createElement(tagName: "input"): HTMLInputElement;
// ... more overloads ...
function createElement(tagName: string): Element {
    // ... code goes here ...
}
Discriminated Unions

union,guard type,alias一起使用來(lái)構(gòu)建一個(gè)高級(jí)的模式:discriminated unions,也被成為agged unions,或algebraic data types球恤。

discriminated unions在函數(shù)式編程中很有用孔轴。有些編程語(yǔ)言自動(dòng)區(qū)分unions,而TypeScript是構(gòu)建在現(xiàn)代JavaScript上的碎捺,所以不自動(dòng)區(qū)分路鹰。

discriminated unions包括以下三個(gè)要素。

  • 有著相同的收厨,字符串字面量的屬性的類(lèi)型集 - the discriminant.
interface Square {
    kind: "square";
    size: number;
}
interface Rectangle {
    kind: "rectangle";
    width: number;
    height: number;
}
interface Circle {
    kind: "circle";
    radius: number;
}
  • 用一個(gè)類(lèi)型別名把這些類(lèi)型union晋柱, - the union.
type Shape = Square | Rectangle | Circle;
  • 通過(guò)type guards來(lái)對(duì)這個(gè)相同屬性名下的不通知作判斷。
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;
    }
}
Exhaustiveness checking
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末诵叁,一起剝皮案震驚了整個(gè)濱河市雁竞,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌拧额,老刑警劉巖碑诉,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異侥锦,居然都是意外死亡进栽,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)恭垦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)快毛,“玉大人,你說(shuō)我怎么就攤上這事番挺∵氲郏” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵玄柏,是天一觀的道長(zhǎng)襟衰。 經(jīng)常有香客問(wèn)我,道長(zhǎng)粪摘,這世上最難降的妖魔是什么瀑晒? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任阀湿,我火速辦了婚禮,結(jié)果婚禮上瑰妄,老公的妹妹穿的比我還像新娘陷嘴。我一直安慰自己,他們只是感情好间坐,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開(kāi)白布灾挨。 她就那樣靜靜地躺著,像睡著了一般竹宋。 火紅的嫁衣襯著肌膚如雪劳澄。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,287評(píng)論 1 301
  • 那天蜈七,我揣著相機(jī)與錄音秒拔,去河邊找鬼。 笑死飒硅,一個(gè)胖子當(dāng)著我的面吹牛砂缩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播三娩,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼庵芭,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了雀监?” 一聲冷哼從身側(cè)響起双吆,我...
    開(kāi)封第一講書(shū)人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎会前,沒(méi)想到半個(gè)月后好乐,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瓦宜,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年蔚万,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片歉提。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡笛坦,死狀恐怖区转,靈堂內(nèi)的尸體忽然破棺而出苔巨,到底是詐尸還是另有隱情,我是刑警寧澤废离,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布侄泽,位于F島的核電站,受9級(jí)特大地震影響蜻韭,放射性物質(zhì)發(fā)生泄漏悼尾。R本人自食惡果不足惜柿扣,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望闺魏。 院中可真熱鬧未状,春花似錦、人聲如沸析桥。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)泡仗。三九已至埋虹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間娩怎,已是汗流浹背搔课。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留截亦,地道東北人爬泥。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像崩瓤,于是被迫代替她去往敵國(guó)和親急灭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容