#Issue with correlated unions

17 messages · Page 1 of 1 (latest)

steady plankBOT
#
Daniell#4062

Preview:```ts
import {} from "discord-api-types"
import {
Awaitable,
ClientEvents,
Events,
Client,
} from "discord.js"

export const createEvent = <
T extends keyof ClientEvents

(event: {
event: T
callback: (
...args: ClientEvents[T]
) => Awaitable<void>
}) => event
...```

primal citrus
#

Any idea how to fix this?

#

Issue with correlated unions

dusky viper
#

not really, no

#

good old

#

!:corr*

steady plankBOT
#
Retsam19#2505
`!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);
dusky viper
#

you can do the old trick of extracting things out to a generic

steady plankBOT
#
n_n#2622

Preview:```ts
...
function call<
Fn extends (...args: Args) => unknown,
Args extends unknown[]

(fn: Fn, ...args: Args) {
fn(...args)
}

events.forEach(e =>
call(client.on, e.event, e.callback)
)```

dusky viper
#

which i believe is actually unsound

steady plankBOT
#
n_n#2622

Preview:ts ... events.forEach(e => call( client.on, Events.ClientReady, (x: Interaction<CacheType>) => {} ) )

dusky viper
#

demonstration of the unsoundness

#

so arguably the better way to go is to just do as any and leave a big warning sign or something like that

#

the only way i'm aware, of telling typescript this is type-safe, is by handling each possible union member individually

steady plankBOT
#
n_n#2622

Preview:ts ... events.forEach(({event, callback}) => { switch (event) { case Events.ClientReady: { client.on(event, callback) break } case Events.InteractionCreate: { client.on(event, callback) } } })

dusky viper
#

like this

primal citrus
#

Thank you