简体   繁体   English

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

[英]Stricter union types when using mapped types in TypeScript

I'm attempting to use a mapped type to provide more type safety when using union types in a map.在映射中使用联合类型时,我试图使用映射类型来提供更多类型安全。 There does not seem to be a way to provide type safety between the key/value when using a property type (eg ['value'] ) as the type for the key ( K ).当使用属性类型(例如['value'] )作为键( K )的类型时,似乎没有办法在键/值之间提供类型安全。

I would like to avoid creating a unique model by hand to allow for this.我想避免手动创建一个独特的模型来实现这一点。

Code:代码:

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
}

I have commented the line I expected to fail.我已经评论了我预计会失败的行。

I feel I should be able to leverage the key ( K in ) to provide the correct type as the value.我觉得我应该能够利用键( K in )来提供正确的类型作为值。 However, I currently use A , which provides the IAction implementation where the value is of type string - I want to avoid this.但是,我目前使用A ,它提供IAction实现,其中valuestring类型 - 我想避免这种情况。

Is this possible in the current version of TypeScript?这在当前版本的 TypeScript 中可能吗?

Yes, it is possible to do what you want.是的,可以做你想做的。

As you note, your problem is that the values of properties of ActionMapper<A> is always A .正如您所注意到的,您的问题是ActionMapper<A>的属性值始终为A For Actions , that's a union type.对于Actions ,这是一个联合类型。 What you really want to do is extract from A the constituent that matches {value: K} for each key K .您真正想要做的是从A提取与每个键K匹配{value: K}的成分。 Luckily, there is a pre-defined conditional type named Extract that does this for you.幸运的是,有一个名为Extract预定义条件类型可以为您执行此操作。 Let's redefine ActionMapper<A> :让我们重新定义ActionMapper<A>

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

Now we will try again:现在我们再试一次:

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"'.
}

And you get the error you expected.你会得到你预期的错误。 Hope that helps.希望有帮助。 Good luck!祝你好运!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM