首页 > 解决方案 > TypeScript 无法使用 keyof 作为属性从泛型中正确推断类型

问题描述

我目前正在为验证组件编写一些 typedef 和类,该组件正在构建以验证特定类(下面的“Main”)上的字段。目标:

  1. 每个验证规则 ( MainValidationRule) 应该包含T extends keyof Main它正在验证的键 ( ),以及验证相应值并返回指示有效性 ( (value: Main[T]) => boolean) 的标志的验证函数。
  2. 为了便于使用验证器,在运行时,验证规则数组将被组装成一个对象,将每个字段 ( 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
});

标签: typescripttypescript-generics

解决方案


有时最好使用联合类型而不是Foo<keyof Bar>

为了清楚起见,只需比较我的MainValidationRuleMainValidationRule<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
});

操场


推荐阅读