#Function union type resulting in never argument

13 messages · Page 1 of 1 (latest)

bleak spire
#

a function of type Command might require an argument of "a" or "b" or "c" or "d"

#

Therefore typescript requires to you pass a value of type that matches all of those

#

"a" & "b" & "c" & "d"

bright lanternBOT
#
type A = "a" & "b" & "c" & "d"
//   ^? - type A = never
#
type Letter = "a" | "b" | "c" | "d";

type CommandBase<T extends Letter> = (letter: T) => void;

type ACommand = CommandBase<"a">;
type BCommand = CommandBase<"b">;
type CCommand = CommandBase<"c">;
type DCommand = CommandBase<"d">;

type Command = ACommand | BCommand | CCommand | DCommand;

interface CommandContainer {
    execute: Command;
}

let execute = ((letter: "d") => {
    console.log(letter);
}) satisfies Command

let container = {
    execute: execute
} satisfies CommandContainer


const letter: "d" = "d";

execute(letter);
container.execute(letter);
bleak spire
#

This might help you

bleak spire
#

satisfies checks that a value matches a given type, but TS actually saves the value as the specific inferred type instead of widening it

bright lanternBOT
#
const a: string = 'foo'
//    ^? - const a: string
const b = 'bar' satisfies string
//    ^? - const b: "bar"
bleak spire
#

if you've retrieved a command from a array of type CommandContainer[], you don't know which type of Command it is. What you're trying to do here is just not logically correct

#

if the commands are always DCommand, use DCommand[]

#

If they aren't always DCommand, you can't safely call it with "d"

#

You could save some extra information so that you can identify which it is

bright lanternBOT
#
sandiford#0

Preview:```ts
type Letter = "a" | "b" | "c" | "d"

type CommandBase<T extends Letter> = (
letter: T
) => void

type ACommand = CommandBase<"a">
type BCommand = CommandBase<"b">
type CCommand = CommandBase<"c">
type DCommand = CommandBase<"d">

type Command =
| ACommand
| BCommand
| CCommand
| DCommand
...```