是否有可能創建一個 TypeScript 型別保護函式,以確定給定的鍵是否在給定的(通用)物件中 - 非常類似于 key in obj,但作為一個功能性的型別保護(由于與本問題無關的原因需要)。
例如,像這樣的東西:
export function has<T extends { [index: string]: any; [index: number]: any }>(
obj: T。
property: string | symbol | number
): 屬性是T的鍵 {
return Object.prototype. hasOwnProperty.call(obj, property)
}
//然后在用戶地某處:。
介面 Foo {
bar: string
}
介面 Fuzz {
buzz: string
}
function doWork(thing: Foo | Fuzz) {
if (has(thing, 'bar') {
alert(thing.bar) // ideally we've type narrowed to know thing contains foo.
}
}
上面的代碼沒有按照我所期望的那樣作業(alert(thing.foo))不知道foo的存在- 顯然我的型別保護宣告property is keyof T沒有達到我所期望的效果。
你可以對結果進行型別保護,使其僅為 uj5u.com熱心網友回復: 我們希望 如果 如果 如果 如果Foo 或 Fuzz - 但是我特別想要型別保護一個特定的鍵存在于一個泛型上。
has(obj, k)能夠作為用戶自定義的型別保護函式。 我們試圖用has(obj, k)來支持的用例是:
k是一個單一的字面型別,并且obj是一個物件型別的union,其中一些union成員明確地以k為鍵,那么一個true結果應該將obj縮小到只有那些以k為鍵的成員,而一個false結果應該將obj縮小到只有那些沒有k作為鍵的成員。 這就是目前in運算子通過k in obj縮小物件的方法。 它不是聲音,因為structural subtyping意味著一個{x: string}型別的物件很可能有比x更多的鍵,所以當你檢查一個例如y的鍵時,你不能安全地從可能性的串列中排除{x: string}。 但是這就是今天in運算子的作業方式,所以我們不妨對has()做同樣的事情。k是一個單一的字面型別,并且obj是不是一個聯合型別,并且如果那個非聯合型別在鍵k處沒有一個明確的屬性值,那么一個true結果應該縮小obj在鍵k處有一個明確的unknown屬性值。 一個false結果不應該縮小obj。 這不是目前TypeScript中的in運算子縮小,盡管在microsoft/TypeScript#21732有一個建議來支持這個。k是一個廣泛的屬性型別,如string、number、symbol或PropertyKey,那么對于true或false的結果,我們根本不希望縮小obj。 在這種情況下,我們可能希望縮小k的范圍,但是這并沒有被明確地作為一個用例,所以我將不追究。 現在我想說的是,在這種情況下,has()的回傳值將只是boolean。
k是屬于字面型別的union,那么我們大概想以與前兩種情況相同的方式縮小obj。如果obj是一個聯盟,那么就把obj縮小到只有那些有/沒有與k的任何可能值相匹配的聯盟成員;如果obj不是一個聯盟,那么就把obj縮小到本身就是一個物件型別的聯盟,而k的聯盟的每個可能成員就是結果中的一個成員。 這并不是一個明確的要求,但是這樣做比我能想到的任何其他方法都要好(最簡單的實作has()回傳true將把obj縮小為具有所有鍵的k聯盟的東西,這是相當差的)。
考慮到這些用例,下面是has()的一個潛在實作:
export function has< T extends object, K extends PropertyKey> (
obj: T,
property: RequireLiteral<K>
): obj是T & { [P in K]: { [Q in P]: unknown } }[K];
export function has(obj: any, property: PropertyKey) : boolean;
export function has(obj: any, property: propertyKey) {
return Object.prototype。 hasOwnProperty.call(obj, property)
}
型別 RequireLiteral<K extends PropertyKey> =
string extends K ? never :
數字 extends K ? never :
符號 extends K ? never :
K
這是一個多載函式,其中第一個呼叫簽名只在property是字面型別或字面型別的聯合的情況下呼叫。 RequireLiteral<K>型別函式將在這種情況下回傳K,否則它將回傳never。 在任何情況下,回傳型別謂詞型別將obj縮小到其原始型別的交集,以及在K中的每個鍵上具有unknown屬性的型別。 那個{[P in K]:{[Q in P]:unknown}}[K]型別可能更容易用例子來描述:如果K是"a",那么它就是{a: unknown};如果K是"a" | "b",那么它就是{a: unknown} | {b: unknown}。 這個呼叫簽名應該導致我們想要支持的所有行為,其中property不是一個寬型別。
第二個呼叫簽名僅在property是一個寬型別(如string或PropertyKey)時被呼叫。 如果是這樣的話,該函式就不會作為一個型別保護。
我們可以驗證所述的例子是否如愿以償:
我們可以驗證所述的例子是否如愿以償。
function doWork(thing: Foo | Fuzz){
if (has(thing, 'bar') {
// Foo
thing.bar。
} else {
// Fuzz
things.buzz
}
}
const x: { [index: string] : any } = {
baz: 123: any
}
if (!has(x, 'buzz') {
/* { [index: string]: any; } */
x.buzz =123
} else {
/* { [index: string]: any; } & { buzz: unknown; } */
x.嗡嗡聲。
}
const y: { [index: string]: any } = {}.
const key: PropertyKey = 'a'.
if (!has(y, key) {
// { [index: string]: any; }
y[key] = 123 else {
// { [index: string]: any; }
y
}
看起來不錯!
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/307927.html
標籤:
上一篇:是否有更好的方法來告訴typescript"資料"是哪種型別?
下一篇:型別化介面的動態屬性
