TypeScript——高級(jí)類(lèi)型(2)

可以為null的類(lèi)型

TypeScript具有兩種特殊的類(lèi)型震鹉, null和 undefined携龟,它們分別具有值null和undefined. 我們?cè)赱基礎(chǔ)類(lèi)型](./Basic Types.md)一節(jié)里已經(jīng)做過(guò)簡(jiǎn)要說(shuō)明狱窘。 默認(rèn)情況下隐轩,類(lèi)型檢查器認(rèn)為 null與 undefined可以賦值給任何類(lèi)型窗慎。 null與 undefined是所有其它類(lèi)型的一個(gè)有效值图云。 這也意味著惯悠,你阻止不了將它們賦值給其它類(lèi)型,就算是你想要阻止這種情況也不行竣况。 null的發(fā)明者克婶,Tony Hoare,稱它為 價(jià)值億萬(wàn)美金的錯(cuò)誤丹泉。

--strictNullChecks標(biāo)記可以解決此錯(cuò)誤:當(dāng)你聲明一個(gè)變量時(shí)情萤,它不會(huì)自動(dòng)地包含 null或 undefined。 你可以使用聯(lián)合類(lèi)型明確的包含它們:

let s = "foo";

s = null; // 錯(cuò)誤, 'null'不能賦值給'string'

let sn: string | null = "bar";

sn = null; // 可以

sn = undefined; // error, 'undefined'不能賦值給'string | null'

注意摹恨,按照J(rèn)avaScript的語(yǔ)義筋岛,TypeScript會(huì)把 null和 undefined區(qū)別對(duì)待。 string | null晒哄, string | undefined和 string | undefined | null是不同的類(lèi)型睁宰。

可選參數(shù)和可選屬性

使用了 --strictNullChecks,可選參數(shù)會(huì)被自動(dòng)地加上 | undefined:

function f(x: number, y?: number) {

? ? return x + (y || 0);

}

f(1, 2);

f(1);

f(1, undefined);

f(1, null); // error, 'null' is not assignable to 'number | undefined'

可選屬性也會(huì)有同樣的處理:

class C {

? ? a: number;

? ? b?: number;

}

let c = new C();

c.a = 12;

c.a = undefined; // error, 'undefined' is not assignable to 'number'

c.b = 13;

c.b = undefined; // ok

c.b = null; // error, 'null' is not assignable to 'number | undefined'

類(lèi)型保護(hù)和類(lèi)型斷言

由于可以為null的類(lèi)型是通過(guò)聯(lián)合類(lèi)型實(shí)現(xiàn)揩晴,那么你需要使用類(lèi)型保護(hù)來(lái)去除 null勋陪。 幸運(yùn)地是這與在JavaScript里寫(xiě)的代碼一致:

function f(sn: string | null): string {

? ? if (sn == null) {

? ? ? ? return "default";

? ? }

? ? else {

? ? ? ? return sn;

? ? }

}

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

function f(sn: string | null): string {

? ? return sn || "default";

}

如果編譯器不能夠去除 null或 undefined硫兰,你可以使用類(lèi)型斷言手動(dòng)去除诅愚。 語(yǔ)法是添加 !后綴: identifier!從 identifier的類(lèi)型里去除了 null和 undefined:

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ù),因?yàn)榫幾g器無(wú)法去除嵌套函數(shù)的null(除非是立即調(diào)用的函數(shù)表達(dá)式)劫映。 因?yàn)樗鼰o(wú)法跟蹤所有對(duì)嵌套函數(shù)的調(diào)用违孝,尤其是你將內(nèi)層函數(shù)做為外層函數(shù)的返回值。 如果無(wú)法知道函數(shù)在哪里被調(diào)用泳赋,就無(wú)法知道調(diào)用時(shí) name的類(lèi)型雌桑。

類(lèi)型別名

類(lèi)型別名會(huì)給一個(gè)類(lèi)型起個(gè)新名字。 類(lèi)型別名有時(shí)和接口很像祖今,但是可以作用于原始值校坑,聯(lián)合類(lèi)型拣技,元組以及其它任何你需要手寫(xiě)的類(lèi)型。

type Name = string;

type NameResolver = () => string;

type NameOrResolver = Name | NameResolver;

function getName(n: NameOrResolver): Name {

? ? if (typeof n === 'string') {

? ? ? ? return n;

? ? }

? ? else {

? ? ? ? return n();

? ? }

}

起別名不會(huì)新建一個(gè)類(lèi)型 - 它創(chuàng)建了一個(gè)新 名字來(lái)引用那個(gè)類(lèi)型耍目。 給原始類(lèi)型起別名通常沒(méi)什么用膏斤,盡管可以做為文檔的一種形式使用。

同接口一樣邪驮,類(lèi)型別名也可以是泛型 - 我們可以添加類(lèi)型參數(shù)并且在別名聲明的右側(cè)傳入:

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

我們也可以使用類(lèi)型別名來(lái)在屬性里引用自己:

type Tree<T> = {

? ? value: T;

? ? left: Tree<T>;

? ? right: Tree<T>;

}

與交叉類(lèi)型一起使用莫辨,我們可以創(chuàng)建出一些十分稀奇古怪的類(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;

然而毅访,類(lèi)型別名不能出現(xiàn)在聲明右側(cè)的任何地方沮榜。

type Yikes = Array<Yikes>; // error

接口 vs. 類(lèi)型別名

像我們提到的,類(lèi)型別名可以像接口一樣喻粹;然而蟆融,仍有一些細(xì)微差別。

其一磷斧,接口創(chuàng)建了一個(gè)新的名字振愿,可以在其它任何地方使用。 類(lèi)型別名并不創(chuàng)建新名字—比如弛饭,錯(cuò)誤信息就不會(huì)使用別名冕末。 在下面的示例代碼里,在編譯器中將鼠標(biāo)懸停在 interfaced上侣颂,顯示它返回的是 Interface档桃,但懸停在 aliased上時(shí),顯示的卻是對(duì)象字面量類(lèi)型憔晒。

type Alias = { num: number }

interface Interface {

? ? num: number;

}

declare function aliased(arg: Alias): Alias;

declare function interfaced(arg: Interface): Interface;

另一個(gè)重要區(qū)別是類(lèi)型別名不能被 extends和 implements(自己也不能 extends和 implements其它類(lèi)型)藻肄。 因?yàn)?軟件中的對(duì)象應(yīng)該對(duì)于擴(kuò)展是開(kāi)放的,但是對(duì)于修改是封閉的拒担,你應(yīng)該盡量去使用接口代替類(lèi)型別名嘹屯。

另一方面,如果你無(wú)法通過(guò)接口來(lái)描述一個(gè)類(lèi)型并且需要使用聯(lián)合類(lèi)型或元組類(lèi)型从撼,這時(shí)通常會(huì)使用類(lèi)型別名州弟。

字符串字面量類(lèi)型

字符串字面量類(lèi)型允許你指定字符串必須的固定值。 在實(shí)際應(yīng)用中低零,字符串字面量類(lèi)型可以與聯(lián)合類(lèi)型婆翔,類(lèi)型保護(hù)和類(lèi)型別名很好的配合。 通過(guò)結(jié)合使用這些特性掏婶,你可以實(shí)現(xiàn)類(lèi)似枚舉類(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)做為參數(shù)傳遞,傳入其它值則會(huì)產(chǎn)生錯(cuò)誤雄妥。

Argument of type '"uneasy"' is not assignable to parameter of type '"ease-in" | "ease-out" | "ease-in-out"'

字符串字面量類(lèi)型還可以用于區(qū)分函數(shù)重載:

function createElement(tagName: "img"): HTMLImageElement;

function createElement(tagName: "input"): HTMLInputElement;

// ... more overloads ...

function createElement(tagName: string): Element {

? ? // ... code goes here ...

}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末最蕾,一起剝皮案震驚了整個(gè)濱河市依溯,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌揖膜,老刑警劉巖誓沸,帶你破解...
    沈念sama閱讀 222,807評(píng)論 6 518
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異壹粟,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)宿百,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,284評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門(mén)趁仙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人垦页,你說(shuō)我怎么就攤上這事雀费。” “怎么了痊焊?”我有些...
    開(kāi)封第一講書(shū)人閱讀 169,589評(píng)論 0 363
  • 文/不壞的土叔 我叫張陵盏袄,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我薄啥,道長(zhǎng)辕羽,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 60,188評(píng)論 1 300
  • 正文 為了忘掉前任垄惧,我火速辦了婚禮刁愿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘到逊。我一直安慰自己铣口,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,185評(píng)論 6 398
  • 文/花漫 我一把揭開(kāi)白布觉壶。 她就那樣靜靜地躺著脑题,像睡著了一般。 火紅的嫁衣襯著肌膚如雪铜靶。 梳的紋絲不亂的頭發(fā)上叔遂,一...
    開(kāi)封第一講書(shū)人閱讀 52,785評(píng)論 1 314
  • 那天,我揣著相機(jī)與錄音旷坦,去河邊找鬼掏熬。 笑死,一個(gè)胖子當(dāng)著我的面吹牛秒梅,可吹牛的內(nèi)容都是我干的旗芬。 我是一名探鬼主播,決...
    沈念sama閱讀 41,220評(píng)論 3 423
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼捆蜀,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼疮丛!你這毒婦竟也來(lái)了幔嫂?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 40,167評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤誊薄,失蹤者是張志新(化名)和其女友劉穎履恩,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體呢蔫,經(jīng)...
    沈念sama閱讀 46,698評(píng)論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡切心,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,767評(píng)論 3 343
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了片吊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绽昏。...
    茶點(diǎn)故事閱讀 40,912評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖俏脊,靈堂內(nèi)的尸體忽然破棺而出全谤,到底是詐尸還是另有隱情,我是刑警寧澤爷贫,帶...
    沈念sama閱讀 36,572評(píng)論 5 351
  • 正文 年R本政府宣布认然,位于F島的核電站,受9級(jí)特大地震影響漫萄,放射性物質(zhì)發(fā)生泄漏卷员。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,254評(píng)論 3 336
  • 文/蒙蒙 一卷胯、第九天 我趴在偏房一處隱蔽的房頂上張望子刮。 院中可真熱鬧,春花似錦窑睁、人聲如沸挺峡。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,746評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)橱赠。三九已至,卻和暖如春箫津,著一層夾襖步出監(jiān)牢的瞬間狭姨,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,859評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工苏遥, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留饼拍,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,359評(píng)論 3 379
  • 正文 我出身青樓田炭,卻偏偏與公主長(zhǎng)得像师抄,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子教硫,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,922評(píng)論 2 361

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