TypeScript推断受限条件键类型的值类型时出现问题
假设我有以下实体模型:TypeScript推断受限条件键类型的值类型时出现问题,typescript,Typescript,假设我有以下实体模型: interface Entity { id: string; } interface Member extends Entity { group: Group | string; } interface Group extends Entity { members: (Member | string)[]; } 我将分别检索组和成员实体的列表。检索时,实体将包含对其他实体的字符串引用。例如,成员实体将具有一个组属性,其中包含该成员所属组的ID 现在,在检
interface Entity {
id: string;
}
interface Member extends Entity {
group: Group | string;
}
interface Group extends Entity {
members: (Member | string)[];
}
我将分别检索组和成员实体的列表。检索时,实体将包含对其他实体的字符串引用。例如,成员实体将具有一个组属性,其中包含该成员所属组的ID
现在,在检索之后,我想通过将字符串引用替换为它们引用的实体来水合/膨胀实体。例如,以下调用应将成员实体中的组引用替换为实际的组对象:
现在,我想将该调用“group”中的第三个参数限制为只允许可能引用组实体的属性名,因此我编写了以下类型定义:
type Ref<T, S> = { [K in keyof T]: S extends T[K] ? K : never }[keyof T];
不幸的是,这会引发编译器错误:
Type 'S | T[{ [K in keyof T]: S extends T[K] ? K : never; }[keyof T]]' is not assignable to type 'T[{ [K in keyof T]: S extends T[K] ? K : never; }[keyof T]]'.
Type 'S' is not assignable to type 'T[{ [K in keyof T]: S extends T[K] ? K : never; }[keyof T]]'.
Type 'Entity' is not assignable to type 'T[{ [K in keyof T]: S extends T[K] ? K : never; }[keyof T]]'.(2322)
我相信,有了TypeScript丰富的类型系统,应该完全有可能想出一个优雅的解决方案,但我似乎无法正确地实现它。我错过了什么
对于那些想尝试一下的人,这里有一个建议
请注意,我正在寻找一个优雅的表意字体解决方案,而不是一个强迫编译器的黑客。如果我想这样做,我不妨编写简单的JavaScript。通常,当实现中的泛型函数中有条件类型和映射类型时,TS将很难始终跟踪这些类型的所有含义。通常,仍然包含未解析类型参数的条件类型或映射类型只能分配给自身,因此S不能分配给T[Ref] 有一种方法可以确保函数类型的实现是安全的,但这是以开发人员经验为代价的:
function link<S extends Entity, K extends PropertyKey>(target: Array<Record<K, string | S>>, source: S[], ref: K) {
const lookup = new Map(source.map<[string, S]>(entity => [entity.id, entity]));
target.forEach(entity => {
const value = entity[ref];
entity[ref] = typeof value === 'string' ? lookup.get(value) || value : value;
});
}
通常,当在实现中的泛型函数中有条件类型和映射类型时,TS将很难始终跟踪这些类型的所有含义。通常,仍然包含未解析类型参数的条件类型或映射类型只能分配给自身,因此S不能分配给T[Ref] 有一种方法可以确保函数类型的实现是安全的,但这是以开发人员经验为代价的:
function link<S extends Entity, K extends PropertyKey>(target: Array<Record<K, string | S>>, source: S[], ref: K) {
const lookup = new Map(source.map<[string, S]>(entity => [entity.id, entity]));
target.forEach(entity => {
const value = entity[ref];
entity[ref] = typeof value === 'string' ? lookup.get(value) || value : value;
});
}
将签名更改为:
function link<T extends Entity, K extends keyof T, S extends T[K] & Entity>(
target: T[], source: S[], ref: K
) {
TypeScript无法推断像S扩展t[{[K in keyof t]:S扩展t[K]?K:never;}[keyof t]]那样复杂的关系。将签名更改为:
function link<T extends Entity, K extends keyof T, S extends T[K] & Entity>(
target: T[], source: S[], ref: K
) {
TypeScript无法推断像S扩展t[{[K in keyof t]:S扩展t[K]?K:never;}[keyof t]]那样复杂的关系
function link<T extends Entity, K extends keyof T, S extends T[K] & Entity>(
target: T[], source: S[], ref: K
) {