#Types declared as const are resulting in unions

6 messages · Page 1 of 1 (latest)

full cape
#

For context, I have this code:

    interface CommandOptions {
        id: number;
        baseCommandName?: string;
        commandName: string;
        runtime: "Server" | "Client";
        commandArgs?: Array<Omit<CommandArg, "type"> & { type: CommandArgType["Type"] }>
    }

    interface CommandArgTypeMap {
        Boolean: boolean;
        SignedInt: number;
        UnsignedInt: number;
        Float: number;
        String: string;
        Player: Player;
    }

    type InferCommandArgs<T extends CommandOptions & { commandArgs: readonly unknown[] }> = 
    {
        [K in T['commandArgs'][number]['name']]: T['commandArgs'][number]['required'] extends true ? CommandArgTypeMap[T['commandArgs'][number]['type']] : CommandArgTypeMap[T['commandArgs'][number]['type']] | undefined; 
    }

const helpCommandOpts = {
    id: 0,
    commandName: "help",
    runtime: "Server",
    commandArgs: [{
        name: "test",
        required: true,
        type: "Boolean"
    }, {
        name: "test2",
        required: false,
        type: "Player"
    }]
} as const satisfies CommandOptions

const args: InferCommandArgs<typeof helpCommandOpts> = ...; // doesn't matter what the value is but looking more at the type

// it currently is
args.test // type is boolean | Player
args.test2 // type is boolean | Player | undefined
// adding more commands just adds on to the unions

// IT SHOULD BE
args.test // type is boolean
args.test2 // type is Player | undefined

I am having issues where it's turning the end result type into a whole bunch of unions when I don't want it like that, I am confused considering the types are the actual values ("test" instead of string)

unborn onyx
#

this could probably be simplified, but here's an attempt:

pale sunBOT
#
mkantor#0

Preview:```ts
// stubs:
type Player = 'Player'
type CommandArg = {
name: string
required: boolean
type: keyof CommandArgTypeMap
}
type CommandArgType = {
Type: keyof CommandArgTypeMap
}

interface CommandOptions {
id: number
baseCommandName?: string
commandName: string
...```

unborn onyx
#

the main trick is that you wanted what's called a "homomorphic mapped type" where you map over the individual tuple elements one at a time. your mapping expression ([K in T['commandArgs'][number]['name']]:) wasn't doing that; instead it was treating the tuple as an array and getting the element type (which was a union of all the possibilities). in order for a mapping expression to be homomorphic it has to look like [A in keyof B]:

full cape
#

ohhh makes more sense

#

thanks for that help!