typescript - TypeScript 无法使用 keyof 作为属性从泛型中正确推断类型
问题描述
我目前正在为验证组件编写一些 typedef 和类,该组件正在构建以验证特定类(下面的“Main”)上的字段。目标:
- 每个验证规则 (
MainValidationRule
) 应该包含T extends keyof Main
它正在验证的键 ( ),以及验证相应值并返回指示有效性 ((value: Main[T]) => boolean
) 的标志的验证函数。 - 为了便于使用验证器,在运行时,验证规则数组将被组装成一个对象,将每个字段 (
T in keyof Main
) 映射到该字段的所有验证规则数组 (Array<MainValidationRule<T>>
)。
但是,当我尝试将规则映射到所需的结构(下面的代码)时,TypeScript 会给出类型错误:
我会假设,因为任何给定的rule.key
保证值都rule
将满足约束(即,我们知道 if rule.key === 'foo'
, thenrule
将满足 type MainValidationRule<'foo'>
,'bar' 或可能添加到的任何其他键也是如此Main
) ,这会编译得很好。但是相反,TypeScript 似乎正在检查是否所有可能的值MainValidationRule<keyof Main>
都是特定键的有效规则,这会失败,因为(例如)MainValidationRule<'bar'>
如果键是有效规则,则该规则不是有效规则'foo'
——尽管事实上我们的约束意味着这永远不可能。
难道我做错了什么?还是有另一种方法可以让 TypeScript 正确推断出满足约束?该类Main
经常使用新属性进行更新,因此手动输入并检查每个可能的变化是不切实际的。代码如下。提前致谢!
type Main = {
foo: string;
bar: number;
};
type MainValidationRule<T extends keyof Main> = {
key: T;
isValid: (value: Main[T]) => boolean;
};
type MainValidationRulesMap = { [ T in keyof Main ]?: Array<MainValidationRule<T>> };
const mainValidationRulesMap: MainValidationRulesMap = {};
const mainValidationRules: Array<MainValidationRule<keyof Main>> = [];
mainValidationRules.forEach(rule => {
mainValidationRulesMap[rule.key] = [ rule ]; // type error
});
解决方案
有时最好使用联合类型而不是Foo<keyof Bar>
为了清楚起见,只需比较我的MainValidationRule
和MainValidationRule<keyof Main>
。
看起来他们是平等的,但事实并非如此。
对于 TS,推断简单的联合类型要容易得多。
这是代码:
type Main = {
foo: string;
bar: number;
};
type Values<T> = T[keyof T]
type MainValidationRule = Values<{
[P in keyof Main]: {
key: P;
isValid: (value: Main[P]) => boolean;
}
}>
type Rules = Array<MainValidationRule>
type MainValidationRulesMap = Partial<{ [T in keyof Main]: Rules }>;
const mainValidationRulesMap: MainValidationRulesMap = {};
const mainValidationRules: Rules = [];
mainValidationRules.forEach(rule => {
mainValidationRulesMap[rule.key] = [rule]; // ok
});
推荐阅读
- java - 在 Java 代码中输入小数时出现编译错误
- java - 是否可以通过球衣获取带有 Serveur 文件的 HTML 选择?
- vaadin - 如何获取vaadin-grid的行索引
- python-2.7 - 使用 if 语句根据另一个数据框中的值在数据框中创建一列
- sql - UniGrid 绑定
- jquery - 仅对 1200 像素及以上的屏幕启用悬停效果
- python - 仪表板页面打不开
- php - 在 url Laravel 5.5 中仅显示产品名称
- powershell - 将 Get-Childitem 的输出组合到 [System.Reflection.Assembly]::LoadFrom(...)
- r - 在R中一次在目录内创建子目录