Skip to content

Commit 7ee947b

Browse files
authored
feat: support number arg type (#73)
1 parent 02f4fdb commit 7ee947b

File tree

3 files changed

+55
-10
lines changed

3 files changed

+55
-10
lines changed

playground/hello.ts

+19-4
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,31 @@ const command = defineCommand({
1717
type: "boolean",
1818
description: "Use friendly greeting",
1919
},
20-
adjective: {
20+
age: {
21+
type: "number",
22+
description: "Your age",
23+
required: false,
24+
},
25+
adj: {
2126
type: "enum",
2227
description: "Adjective to use in greeting",
2328
options: ["awesome", "cool", "nice"],
2429
default: "awesome",
25-
require: false,
26-
}
30+
required: false,
31+
},
2732
},
2833
run({ args }) {
29-
consola.log(`${args.friendly ? "Hi" : "Greetings"} ${args.adjective} ${args.name}!`);
34+
consola.log(args);
35+
const msg = [
36+
args.friendly ? "Hi" : "Greetings",
37+
args.adj || "",
38+
args.name,
39+
args.age ? `You are ${args.age} years old.` : "",
40+
]
41+
.filter(Boolean)
42+
.join(" ");
43+
44+
consola.log(msg);
3045
},
3146
});
3247

src/args.ts

+15-2
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,11 @@ export function parseArgs<T extends ArgsDef = ArgsDef>(
1010
const parseOptions = {
1111
boolean: [] as string[],
1212
string: [] as string[],
13+
number: [] as string[],
1314
enum: [] as (number | string)[],
1415
mixed: [] as string[],
1516
alias: {} as Record<string, string | string[]>,
16-
default: {} as Record<string, boolean | string>,
17+
default: {} as Record<string, boolean | number | string>,
1718
};
1819

1920
const args = resolveArgs(argsDef);
@@ -23,7 +24,7 @@ export function parseArgs<T extends ArgsDef = ArgsDef>(
2324
continue;
2425
}
2526
// eslint-disable-next-line unicorn/prefer-switch
26-
if (arg.type === "string") {
27+
if (arg.type === "string" || arg.type === "number") {
2728
parseOptions.string.push(arg.name);
2829
} else if (arg.type === "boolean") {
2930
parseOptions.boolean.push(arg.name);
@@ -49,6 +50,7 @@ export function parseArgs<T extends ArgsDef = ArgsDef>(
4950
});
5051

5152
for (const [, arg] of args.entries()) {
53+
// eslint-disable-next-line unicorn/prefer-switch
5254
if (arg.type === "positional") {
5355
const nextPositionalArgument = positionalArguments.shift();
5456
if (nextPositionalArgument !== undefined) {
@@ -74,6 +76,17 @@ export function parseArgs<T extends ArgsDef = ArgsDef>(
7476
"EARG",
7577
);
7678
}
79+
} else if (arg.type === "number") {
80+
const _originalValue = parsedArgsProxy[arg.name];
81+
parsedArgsProxy[arg.name] = Number.parseFloat(
82+
parsedArgsProxy[arg.name] as string,
83+
);
84+
if (Number.isNaN(parsedArgsProxy[arg.name])) {
85+
throw new CLIError(
86+
`Invalid value for argument: \`--${arg.name}\` (\`${_originalValue}\`). Expected a number.`,
87+
"EARG",
88+
);
89+
}
7790
} else if (arg.required && parsedArgsProxy[arg.name] === undefined) {
7891
throw new CLIError(`Missing required argument: --${arg.name}`, "EARG");
7992
}

src/types.ts

+21-4
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,14 @@
11
// ----- Args -----
22

3-
export type ArgType = "boolean" | "string" | "positional" | "enum" | undefined;
3+
export type ArgType =
4+
| "boolean"
5+
| "string"
6+
| "number"
7+
| "enum"
8+
| "positional"
9+
| undefined;
410

5-
export type _ArgDef<T extends ArgType, VT extends boolean | string> = {
11+
export type _ArgDef<T extends ArgType, VT extends boolean | number | string> = {
612
type?: T;
713
description?: string;
814
valueHint?: string;
@@ -14,17 +20,22 @@ export type _ArgDef<T extends ArgType, VT extends boolean | string> = {
1420

1521
export type BooleanArgDef = Omit<_ArgDef<"boolean", boolean>, "options">;
1622
export type StringArgDef = Omit<_ArgDef<"string", string>, "options">;
23+
export type NumberArgDef = Omit<_ArgDef<"number", boolean>, "options">;
24+
export type EnumArgDef = _ArgDef<"enum", string>;
1725
export type PositionalArgDef = Omit<
1826
_ArgDef<"positional", string>,
1927
"alias" | "options"
2028
>;
21-
export type EnumArgDef = _ArgDef<"enum", string>;
29+
2230
export type ArgDef =
2331
| BooleanArgDef
2432
| StringArgDef
33+
| NumberArgDef
2534
| PositionalArgDef
2635
| EnumArgDef;
36+
2737
export type ArgsDef = Record<string, ArgDef>;
38+
2839
export type Arg = ArgDef & { name: string; alias: string[] };
2940

3041
export type ParsedArgs<T extends ArgsDef = ArgsDef> = { _: string[] } & Record<
@@ -37,6 +48,12 @@ export type ParsedArgs<T extends ArgsDef = ArgsDef> = { _: string[] } & Record<
3748
}[keyof T],
3849
string
3950
> &
51+
Record<
52+
{
53+
[K in keyof T]: T[K] extends { type: "number" } ? K : never;
54+
}[keyof T],
55+
number
56+
> &
4057
Record<
4158
{
4259
[K in keyof T]: T[K] extends { type: "boolean" } ? K : never;
@@ -51,7 +68,7 @@ export type ParsedArgs<T extends ArgsDef = ArgsDef> = { _: string[] } & Record<
5168
[K in keyof T]: T[K] extends { options: infer U } ? U : never;
5269
}[keyof T]
5370
> &
54-
Record<string, string | boolean | string[]>;
71+
Record<string, string | number | boolean | string[]>;
5572

5673
// ----- Command -----
5774

0 commit comments

Comments
 (0)