#Intersection was reduce to never because of property has conflicting types

14 messages · Page 1 of 1 (latest)

wind jungle
#

Good evening,

I'd like to create a function, which when passed an 'action' runs the relevant function from a map of action types. The actions use a union and the map extracts the relevant type from the union.

TypeScript, however, doesn't quite understand that the action is being used to index the map and therefore is a valid object to pass to the function.

I've attached a simple repro below:
https://www.typescriptlang.org/play/?ts=5.9.2#code/C4TwDgpgBAggxsAlgewHZQLwCgpQD5QDeUokAXFAETxJoDyqElA3FKgIYC2EFAzsACdEqAOZQAvjnxES4HlRopUAFQDuyFlHYj5qAK6cARhAETmWLHDT8tCJQCU9qRgN4VCUgNoBpKMNh2aJ6UpEwAumEUABTsgagUAKIAHoKxwAA8imgANDKhFL7iAHwAlJhFUABuyIgAJubimERSWagM8jFxZRgVaUoAdBzc2S1xasjRfWjdvXH92hAj4uaW1sBQAk6tTZ208QF7M824iABmULsOTi68nlOo-aFhZR64uFaoNpvOJk33jj9XHc5k9zG8oAB6CHggB6AH4pLhvi5LtMwRIsMsLN9WlFiPkFHF2pRckN5JRlBB+JQJCVzDi4njZORCXtxiStDoKABmAAMtPMQA

Thanks

spark zealotBOT
#

@wind jungle Here's a shortened URL of your playground link! You can remove the full link from your message.

smashman#0

Preview:```ts
type Action =
| {type: "ActionOne"; name: string}
| {type: "ActionTwo"; age: number}

const actionRunners: {
[K in Action["type"]]: (
action: Extract<Action, {type: K}>
) => void
} = {
ActionOne: action => action.name,
ActionTwo: action => action.age
...```

short blade
#

!:corr

spark zealotBOT
#
retsam19#0
`!retsam19:correspondence-problem`:

There's a particular pattern that is safe but hard for the Typescript compiler to handle, which I call the "correspondence problem":

const functionsWithArguments = [
  { func: (arg: string) => {}, arg: "foo" },
  { func: (arg: number) => {}, arg: 0 },
];

for (const { func, arg } of functionsWithArguments) {
  func(arg);
//     ^^^
// Argument of type 'string | number' is not assignable to parameter of type 'never'.
//   Type 'string' is not assignable to type 'never'.
}

The problem is that func is typed as (x: string) => void | (x: number) => void and arg is string | number, but the compiler can't prove that they "correspond": that, for example, arg is only a string when func accepts strings.

As far as the type are concerned, arg could be number, and func could be (arg: string) => void, and that would be a type-error. It's easy for us to see that that won't happen, but that requires understanding the program at a higher-level than the level the compiler operates.

Depending on the specifics there's sometimes clever fixes, but usually I recommend using a type assertion and ignoring the issue:

func(arg as never);
short blade
#

It looks like a variant on this

wind jungle
#

Yep, seems to track

#
as never```
Thanks I hate it 🤣
#

So be it 🙂

#

Cheers, @short blade. Glad to know it's not a simple thing to resolve and a difficult problem with TS

short blade
#

Yeah, a big example of something that feels like it should work, but is a lot more complex from a types perspective.

wind jungle
#

Is there a page online that you'd recommend that describes this issue so I can put it in the code comment?

#

If not, that's alright, just thought I'd see if you had one to hand

short blade
#

Ah, yeah, it does look like TS can handle this version now:

const runAction = <T extends Action["type"]>(action: Extract<Action, { type: T}>) => {
  if (actionRunners[action.type]) {
    const runner = actionRunners[action.type];
    runner(action);
  }
};