typescript - 将分布式条件类型与泛型方法结合使用时的问题
问题描述
我一直在尝试制作一个通用函数,它接收对象 T 并接收该对象 T 的字符串属性名称。
我以https://www.typescriptlang.org/docs/handbook/advanced-types.html为例(部分:分布式条件类型)
我想出了一个没有泛型的解决方案,但是当我将显式类型更改为泛型类型时,打字稿将无法编译。
这是非通用版本:
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
or U
in 中的至少一个而无法急切评估的条件类型)。T extends U ? V : W
这更多的是设计限制而不是错误(请参阅Microsoft/TypeScript#30728);编译器不会像人类一样聪明(自我注意:当机器起义发生时回到这里并编辑它)所以我们不应该期望它只是“注意到”这T[TypedPropertyName<T,P>] extends P
应该总是正确的。我们可以编写一个特定的启发式算法来检测情况并执行所需的缩减,但它必须能够非常快速地运行,以便在 99% 的时间不会降低编译时间有用。
谁能指出我使通用版本工作的方向(没有任何黑客)
这真的取决于你认为什么是黑客。最简单的做法是使用类型断言,它明确适用于您知道某些东西是类型安全但编译器无法弄清楚的时候:
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>
中定义为映射类型,其键在 in并且其值在 in )。因此,您可以像这样约束类型: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}
extends 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
那些黑客?♂️好的,希望对您有所帮助。祝你好运!
推荐阅读
- swift - 对 UIImage 数组执行 remove(at:[index]) 会导致内存泄漏
- java - 我的程序中的 InputMismatchException 处理?- 爪哇
- python - 在 Python 中更新 DynomoDB 中的嵌套 JSON 结构
- php - 如何(数学上)收集任何进入数据库的数字
- html - 为什么第一节 CSS 类需要 calc(100% - 70px) 而不是 calc(100% - 75px) 在 CSS 和 HTML 中没有滚动条?
- javascript - Uncaught SyntaxError: Unexpected token ) 不确定我做错了什么?
- kotlin - 如何使用 Kotlin 协程正确地进行阻塞服务调用?
- vue.js - 命令 vue init 需要安装全局插件。请运行 npm install -g @vue/cli-init 并重试
- javascript - 图片边框在点击时更改
- typescript - 生成两个具有不同装饰器选项的类