结合使用分布式条件类型和泛型方法时出现的问题 [英] Issue when using distributive conditional types combined with generic method
问题描述
我一直在尝试创建一个泛型函数,该函数接收AND对象T并接收该对象T的字符串属性名。
我以https://www.typescriptlang.org/docs/handbook/advanced-types.html为例(部分:分布式条件类型)
我已经提出了一个不使用泛型的解决方案,但是当我将显式类型更改为泛型类型时,TypeScript将无法编译。
这是非通用版本:
export type TypedPropertyNames<T, P> = { [K in keyof T]: T[K] extends P ? K : never }[keyof T];
export type StringPropertyNames<T> = TypedPropertyNames<T, string>;
interface Test {
test: string;
}
function non_generic(form: Test, field: StringPropertyNames<Test>): string {
return form[field];
}
这行得通。
现在,当我将Test接口更改为泛型参数时,它将不再编译。
export type TypedPropertyNames<T, P> = { [K in keyof T]: T[K] extends P ? K : never }[keyof T];
export type StringPropertyNames<T> = TypedPropertyNames<T, string>;
function generic<T>(form: T, field: StringPropertyNames<T>): string {
return form[field]; // This won't compile
}
这是预期行为吗?或者这是一个打字错误? 有没有人能给我指一下让通用版本正常工作的方向(没有任何破解)
更新1:
编译错误:
Type 'T[{ [K in keyof T]: T[K] extends string ? K : never; }[keyof T]]' is not assignable to type 'string'.
推荐答案
编译器通常无法确定未解析条件类型的可赋值(即,由于T extends U ? V : W
中的T
或U
中的至少一个尚未完全指定而无法急于评估的条件类型)。
这更多的是一个设计限制,而不是bug(请参阅Microsoft/TypeScript#30728);编译器将不会像人类那样聪明(请注意:当机器起义发生时请回到这里并编辑此内容),因此我们不应期望它只会"注意"T[TypedPropertyName<T,P>] extends P
应该始终为真。我们可以编写特定的启发式算法来检测这种情况并执行所需的缩减,但它必须能够非常快速地运行,以便在其无用的99%的时间内不会降低编译时间。
有没有人能给我指一下让通用版本正常工作的方向(没有任何破解)
这真的取决于您认为什么是黑客攻击。绝对最简单的做法是使用type assertion,它显式用于您知道某些内容是类型安全的,但编译器无法找出它的时候:
function generic<T>(form: T, field: StringPropertyNames<T>): string {
return form[field] as any as string; // I'm smarter than the compiler 🤓
}
或者您可以尝试引导编译器完成必要的步骤,以了解您正在做的操作是安全的。具体地说,编译器不理解Record<K, V>[K]
可赋值给V
(其中Record<K, V>
在the standard library中定义为其键在K
中且其值在V
中的映射类型)。因此,您可以像这样约束类型T
:
function generic<T extends Record<StringPropertyNames<T>, string>>(
form: T,
field: StringPropertyNames<T>
): string {
return form[field]; // okay
}
现在编译器很高兴了。约束T extends Record<StringPropertyNames<T>, string>
实际上根本不是约束,因为任何对象类型都会符合它(例如,{a: string, b: number}
扩展Record<'a', string>
)。因此,您应该能够在使用原始定义的任何地方使用它(无论如何对于具体类型T
):
interface Foo {
a: string;
b: number;
c: boolean;
d: "d";
}
declare const foo: Foo;
generic(foo, "a"); // okay
generic(foo, "d"); // okay
那些是黑客吗?🤷♂️好的,希望能帮上忙。祝你好运!
这篇关于结合使用分布式条件类型和泛型方法时出现的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!