Skip to content

Instantly share code, notes, and snippets.

@jrson83
Last active March 6, 2024 05:29
Show Gist options
  • Save jrson83/9e2d5b588cf27a0652dfd3dd932286a2 to your computer and use it in GitHub Desktop.
Save jrson83/9e2d5b588cf27a0652dfd3dd932286a2 to your computer and use it in GitHub Desktop.
typescript-discriminated-unions
// https://dev.to/darkmavis1980/what-are-typescript-discriminated-unions-5hbb
export type Expand<T> = T extends infer O ? { [K in keyof O]: O[K] } : never
export type ParseArgsArgumentConfig = Expand<
{
required?: boolean
desc?: string
} & (
| {
multiple?: false
default?: string
}
| {
multiple: true
default?: string[]
}
)
>
export type ParseArgsArgumentsConfig = Record<string, ParseArgsArgumentConfig>
export type ExtractArgType<T extends ParseArgsArgumentConfig> =
T extends ParseArgsArgumentConfig
? T['multiple'] extends true
? string[]
: string
: never
export type TypedHandlerArgs<T extends ParseArgsArgumentsConfig> = Expand<
{
-readonly [K in keyof T]?: ExtractArgType<T[K]>
} & {
-readonly [K in keyof T as T[K]['required'] extends true
? K
: never]-?: ExtractArgType<T[K]>
}
>
export type Command<
ArgsDef extends
| ParseArgsArgumentsConfig
| undefined = ParseArgsArgumentsConfig,
> = {
name?: string
desc?: string
examples?: string[]
arguments?: ArgsDef
options?: Record<string, unknown>
handler?: (
args: ArgsDef extends ParseArgsArgumentsConfig
? TypedHandlerArgs<ArgsDef>
: never,
opts: any
) => void
}
export type Program<ArgsDef extends ParseArgsArgumentsConfig | undefined> = {
name: string
desc?: string
version?: string
command?: Command<ArgsDef>
}
export const buildCmd = function buildCommand<
const ArgsDef extends ParseArgsArgumentsConfig | undefined,
>(cmd: Command<ArgsDef>) {
return {
name: cmd.name ?? '__default__',
desc: cmd.desc,
examples: cmd.examples ?? [],
arguments: cmd.arguments ?? {},
options: cmd.options ?? {},
handler: cmd.handler,
}
}
export const buildProg = function buildProgramm<
const ArgsDef extends ParseArgsArgumentsConfig | undefined,
>(prog: Program<ArgsDef>) {
return {
name: prog.name,
desc: prog.desc,
version: prog.version,
command:
buildCmd({
...prog.command,
}) ?? {},
}
}
const p = buildProg({
name: 'string-util',
desc: 'test',
version: 'test',
command: {
name: 'string-util',
desc: 'split a string',
arguments: {
foo: {
// - foo will show an error
// Type '{ multiple: true; default: string; }' is not assignable to type 'ParseArgsArgumentConfig'.
// Types of property 'default' are incompatible.
// Type 'string' is not assignable to type 'string[]'.
multiple: true,
default: 'hello',
},
},
options: {
fuu: 'baz',
},
handler(args, opts) {
console.log('args: ', args)
console.log('opts: ', opts)
},
},
})
console.log(p)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment