假設我們有一個類有一個重要的泛型變數T
,而另一個類有兩個欄位,一個被包裹,一個沒有:
class Wrapper<V> {
constructor(public value: V) {
}
clone(): Wrapper<V> {
return new Wrapper(this.value);
}
}
class SomeClass {
value1 = new Wrapper(1);
value2 = 2;
}
然后,我們想要一個方法wrapperValue
,當給定一個物件 ( obj
) 和一個欄位名 ( name
) 時,它回傳由 訪問的包裝器的值obj[name].value
。回傳型別正確很重要。到目前為止,這是我設法提出的:
type WrapperKeyOf<S> = keyof {
[K in keyof S as S[K] extends Wrapper<any> ? K: never]: any
}
type WrapperValueTypeOf<W> = W extends Wrapper<infer V> ? V : never;
function wrapperValue<S, K extends WrapperKeyOf<S>>(
obj: S,
name: K,
): WrapperValueTypeOf<S[K]> {
const wrapper: Wrapper<WrapperValueTypeOf<S[K]>> = obj[name];
return wrapper.value;
}
wrapperValue(new SomeClass(), "value1");
該型別WrapperKeyOf
僅限name
于為S
where S[T]
is a 的鍵Wrapper
,并WrapperValueTypeOf<S[T]>
獲取包裝器型別。
TypeScript 編譯器產生以下錯誤:
Type 'S[K]' is not assignable to type 'Wrapper<WrapperValueTypeOf<S[K]>>'.
Type 'S[keyof { [K in keyof S as S[K] extends Wrapper<any> ? K : never]: any; }]' is not assignable to type 'Wrapper<WrapperValueTypeOf<S[K]>>'.
Type 'S[string] | S[number] | S[symbol]' is not assignable to type 'Wrapper<WrapperValueTypeOf<S[K]>>'.
Type 'S[string]' is not assignable to type 'Wrapper<WrapperValueTypeOf<S[K]>>'.
似乎必須是訪問 aK
的密鑰的事實丟失了。有沒有辦法以某種方式保存這些資訊?S
Wrapper
uj5u.com熱心網友回復:
不幸的是,編譯器無法執行必要的抽象泛型型別分析,以驗證T[KeysMatching<T, V>]
可分配給V
的泛型T
,其中KeysMatching<T, V>
屬性鍵的聯合是T
其屬性值可分配給的V
, 如此 SO 問題中所述。問題是KeysMatching<T, V>
只能用條件型別實作(在那里你會有一個檢查 like T[K] extends V ? K : never
),并且編譯器本質上將依賴于泛型型別引數的條件型別視為opaque,并選擇延遲評估它們,直到使用某些特定型別指定泛型型別引數。這實際上是 TypeScript 的設計限制,并記錄在microsoft/TypeScript#30728和microsoft/TypeScript#31275(可能還有其他)中。
由于您WrapperKeyOf<T>
是 的實作KeysMatching<T, Wrapper<any>>
,這意味著編譯器看不到T[WrapperKeyOf<T>]
可以分配給Wrapper<any>
.
但是,編譯器可以告訴您,當您使用一個鍵索引到表單的映射型別(或實用程式型別的{[P in K]: V}
等效使用)時,您將獲得可分配給的東西。Record<K, V>
K
V
因此,如果您將您的要求重新表述為 constrainingobj
而不是 constraining name
,您將能夠獲得您正在尋找的那種型別安全保證:
function wrapperValue<V, K extends PropertyKey>(
obj: Record<K, Wrapper<V>>,
name: K,
): V {
const wrapper = obj[name];
return wrapper.value; // okay
}
在這里,我們讓name
be 是泛型型別K
,它可以是任何類似鍵的型別,然后我們限制obj
為具有鍵的東西,并且該鍵K
的值是Wrapper<V>
泛型型別引數的型別V
。現在編譯器知道它obj[name].value
是 type V
,所以實作是沒有錯誤的。
并且您的呼叫wrapperValue()
仍然是安全的(盡管當您犯錯時,錯誤現在將變為 onobj
而不是name
):
const result = wrapperValue(new SomeClass(), "value1"); // okay
console.log(result.toFixed(1)); // 1.0
wrapperValue(new SomeClass(), "value2"); // error!
// --------> ~~~~~~~~~~~~~~~
// Type 'number' is not assignable to type 'Wrapper<number>'
wrapperValue(new SomeClass(), "value3"); // error!
// --------> ~~~~~~~~~~~~~~~
// Property 'value3' is missing in type 'SomeClass'
Playground 代碼鏈接
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/470417.html
上一篇:泛型函式的回傳型別
下一篇:使用通用鍵索引物件