在 TypeScript 中使用映射类型时更严格的联合类型

Stricter union types when using mapped types in TypeScript

在映射中使用联合类型时,我正在尝试使用映射类型来提供更多类型安全性。当使用 属性 类型(例如 ['value'])作为键(K)的类型时,似乎没有办法在 key/value 之间提供类型安全。

我想避免手动创建一个独特的模型来实现这一点。

代码:

interface IAction { value: string; }

type ActionMapper<A extends IAction> = {
   [K in A['value']]: A;
}

interface IActionOne { value: 'action_one' }

interface IActionTwo { value: 'action_two' }

type Actions = IActionOne | IActionTwo;

const reducerMap: ActionMapper<Actions> = {
  action_one: { value: 'action_one' },
  action_two: { value: 'action_one' }, // expecting this line to fail
}

我已经评论了我预计会失败的行。

我觉得我应该能够利用键 (K in) 提供正确的类型作为值。但是,我目前使用 A,它提供了 IAction 实现,其中 value 的类型为 string - 我想避免这种情况。

这在当前版本的 TypeScript 中可行吗?

是的,可以随心所欲。

正如您所注意到的,您的问题是 ActionMapper<A> 的属性值始终是 A。对于 Actions,这是联合类型。您真正想要做的是从 A 中提取与每个键 K 匹配 {value: K} 的成分。幸运的是,有一个 pre-defined conditional type named Extract 可以为您做到这一点。让我们重新定义 ActionMapper<A>:

type ActionMapper<A extends IAction> = {
   [K in A['value']]: Extract<A, {value: K}>;
}

现在我们再试一次:

const reducerMap: ActionMapper<Actions> = {
  action_one: { value: 'action_one' },
  action_two: { value: 'action_one' }, // error!
  // Type '"action_one"' is not assignable to type '"action_two"'.
}

并且您得到了预期的错误。希望有所帮助。祝你好运!