Last active
March 6, 2024 05:29
-
-
Save jrson83/9e2d5b588cf27a0652dfd3dd932286a2 to your computer and use it in GitHub Desktop.
typescript-discriminated-unions
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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