網站搜尋

如何在 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 連結