TypeScript 詳解之 TypeScript 類型運(yùn)算符

keyof 運(yùn)算符

簡介

keyof 是一個(gè)單目運(yùn)算符造垛,接受一個(gè)對(duì)象類型作為參數(shù)限番,返回該對(duì)象的所有鍵名組成的聯(lián)合類型民傻。

type MyObj = {
  foo: number,
  bar: string,
};

type Keys = keyof MyObj; // 'foo'|'bar'

上面示例中,keyof MyObj返回MyObj的所有鍵名組成的聯(lián)合類型恋谭,即'foo'|'bar'糠睡。

下面是另一個(gè)例子。

interface T {
  0: boolean;
  a: string;
  b(): void;
}

type KeyT = keyof T; // 0 | 'a' | 'b'

由于 JavaScript 對(duì)象的鍵名只有三種類型疚颊,所以對(duì)于任意對(duì)象的鍵名的聯(lián)合類型就是string|number|symbol狈孔。

// string | number | symbol
type KeyT = keyof any;

對(duì)于沒有自定義鍵名的類型使用 keyof 運(yùn)算符信认,返回never類型,表示不可能有這樣類型的鍵名均抽。

type KeyT = keyof object;  // never

上面示例中嫁赏,由于object類型沒有自身的屬性,也就沒有鍵名到忽,所以keyof object返回never類型。

由于 keyof 返回的類型是string|number|symbol清寇,如果有些場(chǎng)合只需要其中的一種類型喘漏,那么可以采用交叉類型的寫法。

type Capital<T extends string> = Capitalize<T>;

type MyKeys<Obj extends object> = Capital<keyof Obj>; // 報(bào)錯(cuò)

上面示例中华烟,類型Capital只接受字符串作為類型參數(shù)翩迈,傳入keyof Obj會(huì)報(bào)錯(cuò),原因是這時(shí)的類型參數(shù)是string|number|symbol盔夜,跟字符串不兼容负饲。采用下面的交叉類型寫法,就不會(huì)報(bào)錯(cuò)喂链。

type MyKeys<Obj extends object> = Capital<string & keyof Obj>;

上面示例中返十,string & keyof Obj等同于string & string|number|symbol進(jìn)行交集運(yùn)算,最后返回string椭微,因此Capital<T extends string>就不會(huì)報(bào)錯(cuò)了洞坑。

如果對(duì)象屬性名采用索引形式,keyof 會(huì)返回屬性名的索引類型蝇率。

// 示例一
interface T {
  [prop: number]: number;
}

// number
type KeyT = keyof T;

// 示例二
interface T {
  [prop: string]: number;
}

// string|number
type KeyT = keyof T;

上面的示例二迟杂,keyof T返回的類型是string|number,原因是 JavaScript 屬性名為字符串時(shí)本慕,包含了屬性名為數(shù)值的情況排拷,因?yàn)閿?shù)值屬性名會(huì)自動(dòng)轉(zhuǎn)為字符串。

如果 keyof 運(yùn)算符用于數(shù)組或元組類型锅尘,得到的結(jié)果可能出人意料监氢。

type Result = keyof ['a', 'b', 'c'];
// 返回 number | "0" | "1" | "2"
// | "length" | "pop" | "push" | ···

上面示例中,keyof 會(huì)返回?cái)?shù)組的所有鍵名藤违,包括數(shù)字鍵名和繼承的鍵名忙菠。

對(duì)于聯(lián)合類型,keyof 返回成員共有的鍵名纺弊。

type A = { a: string; z: boolean };
type B = { b: string; z: boolean };

// 返回 'z'
type KeyT = keyof (A | B);

對(duì)于交叉類型牛欢,keyof 返回所有鍵名。

type A = { a: string; x: boolean };
type B = { b: string; y: number };

// 返回 'a' | 'x' | 'b' | 'y'
type KeyT = keyof (A & B);

// 相當(dāng)于
keyof (A & B) ≡ keyof A | keyof B

keyof 取出的是鍵名組成的聯(lián)合類型淆游,如果想取出鍵值組成的聯(lián)合類型傍睹,可以像下面這樣寫隔盛。

type MyObj = {
  foo: number,
  bar: string,
};

type Keys = keyof MyObj;

type Values = MyObj[Keys]; // number|string

上面示例中,Keys是鍵名組成的聯(lián)合類型拾稳,而MyObj[Keys]會(huì)取出每個(gè)鍵名對(duì)應(yīng)的鍵值類型吮炕,組成一個(gè)新的聯(lián)合類型,即number|string访得。

keyof 運(yùn)算符的用途

keyof 運(yùn)算符往往用于精確表達(dá)對(duì)象的屬性類型龙亲。

舉例來說,取出對(duì)象的某個(gè)指定屬性的值悍抑,JavaScript 版本可以寫成下面這樣鳄炉。

function prop(obj, key) {
  return obj[key];
}

上面這個(gè)函數(shù)添加類型,只能寫成下面這樣搜骡。

function prop(
  obj:object, key:string
):any {
  return obj[key];
}

上面的類型聲明有兩個(gè)問題拂盯,一是無法表示參數(shù)key與參數(shù)obj之間的關(guān)系,二是返回值類型只能寫成any记靡。

有了 keyof 以后谈竿,就可以解決這兩個(gè)問題,精確表達(dá)返回值類型摸吠。

function prop<Obj, K extends keyof Obj>(
  obj:Obj, key:K
):Obj[K] {
  return obj[key];
}

上面示例中空凸,K extends keyof Obj表示KObj的一個(gè)屬性名,傳入其他字符串會(huì)報(bào)錯(cuò)寸痢。返回值類型Obj[K]就表示K這個(gè)屬性值的類型劫恒。

keyof 的另一個(gè)用途是用于屬性映射,即將一個(gè)類型的所有屬性逐一映射成其他值轿腺。

type NewProps<Obj> = {
  [Prop in keyof Obj]: boolean;
};

// 用法
type MyObj = { foo: number; };

// 等于 { foo: boolean; }
type NewObj = NewProps<MyObj>;

上面示例中两嘴,類型NewProps是類型Obj的映射類型,前者繼承了后者的所有屬性族壳,但是把所有屬性值類型都改成了boolean憔辫。

下面的例子是去掉 readonly 修飾符。

type Mutable<Obj> = {
  -readonly [Prop in keyof Obj]: Obj[Prop];
};

// 用法
type MyObj = {
  readonly foo: number;
}

// 等于 { foo: number; }
type NewObj = Mutable<MyObj>;

上面示例中仿荆,[Prop in keyof Obj]Obj類型的所有屬性名贰您,-readonly表示去除這些屬性的只讀特性。對(duì)應(yīng)地拢操,還有+readonly的寫法锦亦,表示添加只讀屬性設(shè)置。

下面的例子是讓可選屬性變成必有的屬性令境。

type Concrete<Obj> = {
  [Prop in keyof Obj]-?: Obj[Prop];
};

// 用法
type MyObj = {
  foo?: number;
}

// 等于 { foo: number; }
type NewObj = Concrete<MyObj>;

上面示例中杠园,[Prop in keyof Obj]后面的-?表示去除可選屬性設(shè)置。對(duì)應(yīng)地舔庶,還有+?的寫法抛蚁,表示添加可選屬性設(shè)置陈醒。

in 運(yùn)算符

JavaScript 語言中,in運(yùn)算符用來確定對(duì)象是否包含某個(gè)屬性名瞧甩。

const obj = { a: 123 };

if ('a' in obj)
  console.log('found a');

上面示例中钉跷,in運(yùn)算符用來判斷對(duì)象obj是否包含屬性a

in運(yùn)算符的左側(cè)是一個(gè)字符串肚逸,表示屬性名爷辙,右側(cè)是一個(gè)對(duì)象。它的返回值是一個(gè)布爾值朦促。

TypeScript 語言的類型運(yùn)算中膝晾,in運(yùn)算符有不同的用法,用來取出(遍歷)聯(lián)合類型的每一個(gè)成員類型思灰。

type U = 'a'|'b'|'c';

type Foo = {
  [Prop in U]: number;
};
// 等同于
type Foo = {
  a: number,
  b: number,
  c: number
};

上面示例中玷犹,[Prop in U]表示依次取出聯(lián)合類型U的每一個(gè)成員混滔。

上一小節(jié)的例子也提到洒疚,[Prop in keyof Obj]表示取出對(duì)象Obj的每一個(gè)鍵名。

方括號(hào)運(yùn)算符

方括號(hào)運(yùn)算符([])用于取出對(duì)象的鍵值類型坯屿,比如T[K]會(huì)返回對(duì)象T的屬性K的類型油湖。

type Person = {
  age: number;
  name: string;
  alive: boolean;
};

// Age 的類型是 number
type Age = Person['age'];

上面示例中,Person['age']返回屬性age的類型领跛,本例是number乏德。

方括號(hào)的參數(shù)如果是聯(lián)合類型,那么返回的也是聯(lián)合類型吠昭。

type Person = {
  age: number;
  name: string;
  alive: boolean;
};

// number|string
type T = Person['age'|'name'];

// number|string|boolean
type A = Person[keyof Obj];

上面示例中喊括,方括號(hào)里面是屬性名的聯(lián)合類型,所以返回的也是對(duì)應(yīng)的屬性值的聯(lián)合類型矢棚。

如果訪問不存在的屬性郑什,會(huì)報(bào)錯(cuò)。

type T = Person['notExisted']; // 報(bào)錯(cuò)

方括號(hào)運(yùn)算符的參數(shù)也可以是屬性名的索引類型蒲肋。

type Obj = {
  [key:string]: number,
};

// number
type T = Obj[string];

上面示例中蘑拯,Obj的屬性名是字符串的索引類型,所以可以寫成Obj[string]兜粘,代表所有字符串屬性名申窘,返回的就是它們的類型number

這個(gè)語法對(duì)于數(shù)組也適用孔轴,可以使用number作為方括號(hào)的參數(shù)剃法。

還有 55% 的精彩內(nèi)容
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
支付 ¥4.99 繼續(xù)閱讀
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市路鹰,隨后出現(xiàn)的幾起案子玄窝,更是在濱河造成了極大的恐慌牵寺,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件恩脂,死亡現(xiàn)場(chǎng)離奇詭異帽氓,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)俩块,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門黎休,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人玉凯,你說我怎么就攤上這事势腮。” “怎么了漫仆?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵捎拯,是天一觀的道長。 經(jīng)常有香客問我盲厌,道長署照,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任吗浩,我火速辦了婚禮建芙,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘懂扼。我一直安慰自己禁荸,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開白布阀湿。 她就那樣靜靜地躺著赶熟,像睡著了一般。 火紅的嫁衣襯著肌膚如雪陷嘴。 梳的紋絲不亂的頭發(fā)上映砖,一...
    開封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天,我揣著相機(jī)與錄音罩旋,去河邊找鬼啊央。 笑死,一個(gè)胖子當(dāng)著我的面吹牛涨醋,可吹牛的內(nèi)容都是我干的瓜饥。 我是一名探鬼主播,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼浴骂,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼乓土!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤趣苏,失蹤者是張志新(化名)和其女友劉穎狡相,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體食磕,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡尽棕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了彬伦。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滔悉。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖单绑,靈堂內(nèi)的尸體忽然破棺而出回官,到底是詐尸還是另有隱情,我是刑警寧澤搂橙,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布歉提,位于F島的核電站,受9級(jí)特大地震影響区转,放射性物質(zhì)發(fā)生泄漏苔巨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一蜗帜、第九天 我趴在偏房一處隱蔽的房頂上張望恋拷。 院中可真熱鬧资厉,春花似錦厅缺、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至窄刘,卻和暖如春窥妇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背娩践。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來泰國打工活翩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人翻伺。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓材泄,卻偏偏與公主長得像,于是被迫代替她去往敵國和親吨岭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子拉宗,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

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