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
表示K
是Obj
的一個(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ù)剃法。