简体   繁体   中英

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 ).

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. However, I currently use A , which provides the IAction implementation where the value is of type string - I want to avoid this.

Is this possible in the current version of 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 . For Actions , that's a union type. What you really want to do is extract from A the constituent that matches {value: K} for each key K . Luckily, there is a pre-defined conditional type named Extract that does this for you. Let's redefine 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!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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