假设我有一个函数需要使用两个泛型类型:

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

推荐答案

您可以constrain R,因此它必须可分配给Record<KR, T[KL]>,使用the Record utility type表示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,但它恰好是TypeScrip支持的循环形式之一.让我们看看它是如何工作的:

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.

看上go 不错!第一个调用成功是因为{oid: string, score: number}可以赋值给Record<"oid", number>,而第二个调用失败是因为它不能赋值给 Record<"oid", string>.

Playground link to code

Typescript相关问答推荐

根据另一个成员推断类成员的类型

React-Native运行方法通过监听另一个状态来更新一个状态

TypScript手册中的never[]参数类型是什么意思?

对于使用另一个对象键和值类型的对象使用TS Generics

如何使类型只能有来自另一个类型的键,但键可以有一个新值,新键应该抛出一个错误

泛型和数组:为什么我最终使用了`泛型T []`而不是`泛型T []`?<><>

基于键的值的打字动态类型;种类;

如何在另一个参数类型中获取一个参数字段的类型?

TypeScrip原始字符串没有方法

嵌套对象的类型

已解决:如何使用值类型限制泛型键?

获取一个TypeScrip对象,并将每个属性转换为独立对象的联合

如何将别名添加到vitest配置文件?

必需的输入()参数仍然发出&没有初始值设定项&q;错误

如何键入对象转换器

如何使对象同时具有隐式和显式类型

是否使用类型将方法动态添加到类?

React-Router V6 中的递归好友路由

当受其他参数限制时,通用参数类型不会缩小

TypeScript 中的通用函数包装和类型推断