Skip to content

Commit a9bfe77

Browse files
authored
fix: downcast utility type (#75)
1 parent d878fc8 commit a9bfe77

File tree

5 files changed

+48
-20
lines changed

5 files changed

+48
-20
lines changed

.changeset/itchy-avocados-leave.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@eegli/tinyparse": patch
3+
---
4+
5+
Fix downcast utility type to default to `string[]`

src/commands.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
AnyGlobal,
55
CommandArgPattern,
66
DefaultHandler,
7-
Downcast,
7+
DowncastFlag,
88
ErrorHandler,
99
FlagOptions,
1010
FlagValue,
@@ -74,7 +74,7 @@ export class CommandBuilder<
7474

7575
// TODO: Figure out how to make this typecheck properly
7676
return this as unknown as CommandBuilder<
77-
Options & Record<K, Downcast<V>>,
77+
Options & Record<K, DowncastFlag<V>>,
7878
Globals
7979
>;
8080
}

src/types.ts

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export type FlagValue = string | number | boolean | Date;
1616
export type FlagOptions<V extends FlagValue = FlagValue> = {
1717
longFlag: LongFlag;
1818
shortFlag?: ShortFlag;
19-
defaultValue: Downcast<V>;
19+
defaultValue: DowncastFlag<V>;
2020
required?: boolean;
2121
description?: string;
2222
};
@@ -63,8 +63,10 @@ export type Subcommand<Options, Globals, Args> = {
6363
args: Args;
6464
description?: string;
6565
handler: Args extends string[]
66-
? GenericHandler<Options, Globals, Downcast<Args>>
67-
: GenericHandler<Options, Globals, string[]>;
66+
? // Strict type annotation
67+
GenericHandler<Options, Globals, DowncastArgs<Args>>
68+
: // Loose type annotation
69+
GenericHandler<Options, Globals, string[]>;
6870
};
6971

7072
/**
@@ -149,20 +151,14 @@ type RemoveNever<T> = {
149151
[K in keyof T as T[K] extends never ? never : K]: T[K];
150152
};
151153

152-
export type Downcast<T> = T extends unknown[]
153-
? {
154-
[P in keyof T]: T[P] extends number
155-
? number
156-
: T[P] extends string
157-
? string
158-
: T[P] extends boolean
159-
? boolean
160-
: T[P];
161-
}
154+
export type DowncastArgs<T extends string[]> = {
155+
[P in keyof T]: string;
156+
};
157+
158+
export type DowncastFlag<T extends FlagValue> = T extends string
159+
? string
162160
: T extends number
163161
? number
164-
: T extends string
165-
? string
166-
: T extends boolean
167-
? boolean
168-
: T;
162+
: T extends boolean
163+
? boolean
164+
: T;

test/types/index.tst.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ describe('subcommand option and global arguments', () => {
2828
})
2929
.setGlobals(() => ({
3030
database: 'db',
31+
fetch: () => {},
3132
})).subcommand;
3233

3334
type HandlerParams = Parameters<
@@ -37,6 +38,12 @@ describe('subcommand option and global arguments', () => {
3738
type HandlerGlobalParams = HandlerParams['globals'];
3839

3940
test('flags are inferred', () => {
41+
expect<HandlerFlagParams>().type.toBeAssignable<{
42+
foo: string;
43+
bar: number;
44+
baz: boolean;
45+
qux: Date;
46+
}>();
4047
expect<HandlerFlagParams>().type.toMatch<{
4148
foo: string;
4249
bar: number;
@@ -45,8 +52,13 @@ describe('subcommand option and global arguments', () => {
4552
}>();
4653
});
4754
test('globals are inferred', () => {
55+
expect<HandlerGlobalParams>().type.toBeAssignable<{
56+
database: string;
57+
fetch: () => void;
58+
}>();
4859
expect<HandlerGlobalParams>().type.toMatch<{
4960
database: string;
61+
fetch: () => void;
5062
}>();
5163
});
5264
});

test/types/utils.tst.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { describe, expect, test } from 'tstyche';
2+
import { DowncastArgs, DowncastFlag } from '../../src/types';
3+
4+
describe('type utils', () => {
5+
test('downcast args', () => {
6+
expect<DowncastArgs<string[]>>().type.toEqual<string[]>();
7+
expect<DowncastArgs<['a', 'b']>>().type.toEqual<[string, string]>();
8+
});
9+
test('downcast flag', () => {
10+
expect<DowncastFlag<1>>().type.toEqual<number>();
11+
expect<DowncastFlag<'as'>>().type.toEqual<string>();
12+
expect<DowncastFlag<true>>().type.toEqual<boolean>();
13+
expect<DowncastFlag<Date>>().type.toEqual<Date>();
14+
});
15+
});

0 commit comments

Comments
 (0)