如何在 TypeScript 中定義兩個具有相同類型的兩個泛型類型字段?
假設我有一個函數需要使用兩個泛型類型:
function foo<
T,
R,
KL extends keyof T,
KR extends keyof R
>(lft: T, rgh: R, kl: KL, kr: KR): void;
我如何確保 T[KL] 和 R[KR] 具有相同的類型?
例如。
const obj1 = {id: 1, name: "foo"};
const obj2 = {oid: 2, score: 98}
foo(obj1, obj2, 'id', 'oid'); // ok
foo(obj1, obj2, 'name', 'oid'); // compile failed for obj1['name'] and obj2['oid'] with diff types
答案#1
你可以約束 R,使其必須可以指派給 Record<KR, T[KL]>,使用 Record 工具類型來表示 R[KR] 的屬性必須可以指派給 L[KL]。像這樣:
declare function foo<
T,
R extends Record<KR, T[KL]>,
KL extends keyof T,
KR extends keyof R
>(lft: T, rgh: R, kl: KL, kr: KR): void;
是的,這是一個遞迴約束,因為 R 依賴於 KR,而 KR 又依賴於 R,但這恰好是 TypeScript 支持的循環形式之一。讓我們看看它是如何運作的:
const obj1 = { id: 1, name: "foo" };
const obj2 = { oid: 2, score: 98 }
foo(obj1, obj2, 'id', 'oid'); // ok
foo(obj1, obj2, 'name', 'oid'); // error!
// -----> ~~~~
// Argument of type '{ oid: number; score: number; }' is not assignable to
// parameter of type 'Record<"oid", string>'. Types of property 'oid'
// are incompatible.
看起來不錯!第一次調用成功,因為 {oid: string, score: number} 可以指派給 Record<"oid", number>,而第二次失敗,因為它不能指派給 Record<"oid", string>。
程式碼的 Playground 連結
