typescript - 在不使用 any 的情況下訪問對象的子對象
我正在將更好的 TypeScript 應用到一個專案中。我的後端傳來一個複雜的對象,它是一個有數十個屬性的類,其中大多數屬性本身也是類。我們有一個表單,有一個通用的變更處理器,目前裡面有一些 any 才能正常工作,但我希望使用泛型來修正它。
這是我應用程式中模式的簡化版本,但我能夠重現我看到的相同錯誤:
class PhoneNumber {
prefix: number;
exchange: number;
line: number;
}
class Address {
address1: string;
address2: string;
city: string;
}
class User {
phone: PhoneNumber;
address: Address;
}
const user = new User();
function setValue<A extends keyof User, T extends User[A], K extends keyof T, V extends T[K]>(
value: V,
fieldType: new () => T,
fieldName: A,
fieldAttribute: K
) {
if (!user[fieldName]) {
user[fieldName] = new fieldType();
}
const field = user[fieldName];
// error here.
field[fieldAttribute] = value;
}
setValue(408, PhoneNumber, 'phone', 'prefix');
我嘗試了幾種方法,這是我能夠接近解決方案的程度。我能在我的 IDE 中檢查最後的函數調用,看起來泛型被填充為合理的內容:function setValue<"phone", PhoneNumber, "prefix", number>(),但在編譯時我收到一個錯誤:
TS2536:型別 'K' 無法用於索引型別 'User[A]'。
我是不是搞錯方式了?如果我搞不清楚這個,我最終只會把不同類型分開到不同的處理器中。
也有一種情況是某些欄位是物件陣列,這使整個事情變得更加複雜。
答案#1
使用目前 setValue 的參數類型,可以提供一個 子類別 B 作為 fieldType(該類別 繼承 自 user[fieldName] 類別 A),並針對 B 的 fieldAttribute,而該屬性在 A 中可能不存在:
class PhoneNumber2 extends PhoneNumber {
extension: number;
}
// Complies with setValue types,
// but if `user` already has a basic PhoneNumber instance,
// the code within setValue fails
setValue(408, PhoneNumber2, "phone", "extension");
// within setValue:
user["phone"]["extension"] = 408;
// ...but user["phone"] may be a PhoneNumber,
// not necessarily a PhoneNumber2!
在你的情況下,不是使用 K extends keyof T(T 是 fieldType 的返回類型,可能是子類),你可以直接使用 K extends keyof User[A],以確保 fieldAttribute 針對的是基類:
function setValue2<A extends keyof User, T extends User[A], K extends keyof User[A], V extends T[K]>(
value: V,
fieldType: new () => T,
fieldName: A,
fieldAttribute: K
) {
if (!user[fieldName]) {
user[fieldName] = new fieldType();
}
const field = user[fieldName];
field[fieldAttribute] = value; // Now okay
}
setValue2(408, PhoneNumber, 'phone', 'prefix');
setValue2(408, PhoneNumber2, 'phone', 'prefix');
setValue2(408, PhoneNumber2, 'phone', 'extension'); // Error: Argument of type '"extension"' is not assignable to parameter of type 'keyof PhoneNumber'.
你甚至可以去掉通用型別 T:
function setValue3<A extends keyof User, K extends keyof User[A], V extends User[A][K]>(
value: V,
fieldType: new () => User[A],
fieldName: A,
fieldAttribute: K
) {}
遊樂場連結
