我有以下DataStore
具有 Map 型別的兩個屬性的類。我正在嘗試提出一種方法,該方法getEntity
將根據引數回傳正確的資料型別。還嘗試創建一種updateEntity
更新屬性的方法。
type TUUID = string;
interface IUser {
name: string;
type: 'customer' | 'client';
}
interface IProduct {
name: string;
count: number;
type: 'electronics' | 'food' | 'drinks';
}
const _user: IUser = { name: 'John Doe', type: 'customer' };
interface IDataStore {
users: Map<TUUID, IUser>;
products: Map<TUUID, IProduct>;
}
export class DataStore implements IDataStore {
users: IDataStore['users'] = new Map();
products: IDataStore['products'] = new Map();
getEntity<T extends keyof IDataStore>(storeKey: T, id: TUUID) {
if (this[storeKey] && this[storeKey].has(id)) return this[storeKey].get(id);
return null;
}
updateEntity<T extends keyof IDataStore>(storeKey: T, id: TUUID, data: ReturnType<IDataStore[T]['get']>) {
if (this[storeKey]) {
this[storeKey].set(id, data);
return this[storeKey].get(id);
} else {
return null;
}
}
}
const store = new DataStore();
const user = store.getEntity('users', '1234');
const product = store.getEntity('products', 'abcd');
const updatedUser = store.updateEntity('users', '1234', _user);
但是打字稿推斷getEntity
as 的資料型別IUser | IProduct | null | undefined
。嘗試訪問product.count
時,它提供以下錯誤:
Property 'count' does not exist on type 'IUser | IProduct'.
Property 'count' does not exist on type 'IUser'.
此外,這一行this[storeKey].set(id, data);
提供了錯誤:
(parameter) data: IUser | IProduct | undefined
Argument of type 'IUser | IProduct | undefined' is not assignable to parameter of type 'never'.
The intersection 'IUser & IProduct' was reduced to 'never' because property 'type' has conflicting types in some constituents.
Type 'undefined' is not assignable to type 'never'.ts(2345)
如果我進行方法多載,它通常可以作業。但是隨后方法內部的方法引數變成any
了并且容易出錯。我嘗試使用 Record 和純物件,仍然有同樣的問題。但是,如果我還為特定屬性(如 , )執行方法,getUser
它getProduct
會推斷出正確的型別。updateUser
updateProduct
有沒有辦法擁有一個可以正確推斷資料型別的通用方法?
uj5u.com熱心網友回復:
首先,為了this[storeKey].set(id, data)
作業,您需要重構 和 的型別,this
以便data
編譯器將它們視為彼此明顯兼容。在 microsoft/TypeScript#30581 中描述了類似this[storeKey].set()
并data
具有相關聯合型別的一般問題,在microsoft/TypeScript#47109中實作和描述的修復是重構為分布式物件型別。
例如,這是一種重寫的方法IDataStore
:
interface DataMap {
users: IUser;
products: IProduct;
}
type IDataStore = { [K in keyof DataMap]: Map<TUUID, DataMap[K]> }
這看起來像一個無操作,實際上如果你檢查IDataStore
它,它看起來和以前一樣:
/*type IDataStore = {
users: Map<TUUID, IUser>;
products: Map<TUUID, IProduct>;
}*/
但這里重要的是,編譯器知道它以一種它不知道您的原始定義的方式進行IDataStore
通用操作。K extends keyof DataMap
在updateEntity
我們還將型別更改data
為DataMap[K]
而不是ReturnType<IDataStore[K]['get']>
。前者被認為與IDataStore
泛型的定義有關K
,而后者過于復雜,使用了ReturnType<T>
作為條件型別實作的實用程式型別。
到目前為止,這是我們所擁有的:
getEntity<K extends keyof IDataStore>(storeKey: K, id: TUUID) {
return this[storeKey].get(id) ?? null;
} // returns IUser | IProduct | null
updateEntity<K extends keyof IDataStore>(storeKey: K, id: TUUID, data: DataMap[K]) {
this[storeKey].set(id, data); // error! Argument of type
// 'IUser | IProduct' is not assignable to parameter of type 'never'
return data;
}
(請注意,我洗掉了您的存在/不存在測驗,因為您的屬性不是可選的)。這顯然不是一種改進。的輸出型別getEntity()
仍然是一個并集,并且該set()
方法仍然抱怨交集。現在的問題是 of 的型別this
是多型this
型別,編譯器將其視為隱式泛型型別引數。它自動表示實作子類的型別。這會使編譯器感到困惑,并且它看不到與IDataStore
. 編譯器將this[storeKey]
其視為聯合型別,因為它只是擴展this
到其DataStore
類約束。我們希望在以下方面作業IDataStore
這是專門為保持相關性而構建的。
我的方法是在訪問該物業之前安全地擴大this
到:IDataStore
storeKey
getEntity<K extends keyof IDataStore>(storeKey: K, id: TUUID) {
const thiz: IDataStore = this;
return thiz[storeKey].get(id) ?? null;
} // returns NonNullable<DataMap[K]> | null
updateEntity<K extends keyof IDataStore>(storeKey: K, id: TUUID, data: DataMap[K]) {
const thiz: IDataStore = this;
thiz[storeKey].set(id, data); // okay
}
現在一切正常。的型別thiz[storeKey]
現在用泛型K
型別引數表示,因此get()
回傳DataMap[K] | undefined
并set()
接受 a DataMap[K]
。
Playground 代碼鏈接
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/494540.html
上一篇:如何傳遞多個實作相同介面的型別?