Skip to content

Commit 3e05a2e

Browse files
NiGhTTraXsindresorhus
authored andcommitted
Add type information for flags (#122)
1 parent 499d186 commit 3e05a2e

File tree

2 files changed

+55
-41
lines changed

2 files changed

+55
-41
lines changed

index.d.ts

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import {PackageJson} from 'type-fest';
22
import {Options as MinimistOptions} from 'minimist-options';
33

44
declare namespace meow {
5-
interface Options {
5+
interface Options<Flags extends MinimistOptions> {
66
/**
77
Define argument flags.
88
@@ -23,7 +23,7 @@ declare namespace meow {
2323
}
2424
```
2525
*/
26-
readonly flags?: MinimistOptions;
26+
readonly flags?: Flags;
2727

2828
/**
2929
Description to show above the help text. Default: The package.json `"description"` property.
@@ -159,7 +159,17 @@ declare namespace meow {
159159
readonly hardRejection?: boolean;
160160
}
161161

162-
interface Result {
162+
type TypedFlags<Flags extends MinimistOptions> = {
163+
[F in keyof Flags]: Flags[F] extends {type: 'number'}
164+
? number
165+
: Flags[F] extends {type: 'string'}
166+
? string
167+
: Flags[F] extends {type: 'boolean'}
168+
? boolean
169+
: unknown;
170+
};
171+
172+
interface Result<Flags extends MinimistOptions> {
163173
/**
164174
Non-flag arguments.
165175
*/
@@ -168,12 +178,12 @@ declare namespace meow {
168178
/**
169179
Flags converted to camelCase excluding aliases.
170180
*/
171-
flags: {[name: string]: unknown};
181+
flags: TypedFlags<Flags> & {[name: string]: unknown};
172182

173183
/**
174184
Flags converted camelCase including aliases.
175185
*/
176-
unnormalizedFlags: {[name: string]: unknown};
186+
unnormalizedFlags: TypedFlags<Flags> & {[name: string]: unknown};
177187

178188
/**
179189
The `package.json` object.
@@ -236,7 +246,7 @@ const cli = meow(`
236246
foo(cli.input[0], cli.flags);
237247
```
238248
*/
239-
declare function meow(helpMessage: string, options?: meow.Options): meow.Result;
240-
declare function meow(options?: meow.Options): meow.Result;
249+
declare function meow<Flags extends MinimistOptions>(helpMessage: string, options?: meow.Options<Flags>): meow.Result<Flags>;
250+
declare function meow<Flags extends MinimistOptions>(options?: meow.Options<Flags>): meow.Result<Flags>;
241251

242252
export = meow;

index.test-d.ts

Lines changed: 38 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,48 +1,52 @@
1-
import {expectType} from 'tsd';
1+
import {expectAssignable, expectType} from 'tsd';
22
import {PackageJson} from 'type-fest';
33
import meow = require('.');
44
import {Result} from '.';
55

6-
expectType<Result>(meow('Help text'));
7-
expectType<Result>(meow('Help text', {hardRejection: false}));
8-
expectType<Result>(
9-
meow({
10-
flags: {
11-
unicorn: {
12-
type: 'boolean',
13-
alias: 'u'
14-
},
15-
fooBar: {
16-
type: 'string',
17-
default: 'foo'
18-
}
19-
}
20-
})
6+
expectType<Result<never>>(meow('Help text'));
7+
expectType<Result<never>>(meow('Help text', {hardRejection: false}));
8+
expectAssignable<{flags: {foo: number}}>(
9+
meow({flags: {foo: {type: 'number'}}})
2110
);
22-
expectType<Result>(meow({description: 'foo'}));
23-
expectType<Result>(meow({description: false}));
24-
expectType<Result>(meow({help: 'foo'}));
25-
expectType<Result>(meow({help: false}));
26-
expectType<Result>(meow({version: 'foo'}));
27-
expectType<Result>(meow({version: false}));
28-
expectType<Result>(meow({autoHelp: false}));
29-
expectType<Result>(meow({autoVersion: false}));
30-
expectType<Result>(meow({pkg: {foo: 'bar'}}));
31-
expectType<Result>(meow({argv: ['foo', 'bar']}));
32-
expectType<Result>(meow({inferType: true}));
33-
expectType<Result>(meow({booleanDefault: true}));
34-
expectType<Result>(meow({booleanDefault: null}));
35-
expectType<Result>(meow({booleanDefault: undefined}));
36-
expectType<Result>(meow({hardRejection: false}));
11+
expectAssignable<{flags: {foo: string}}>(
12+
meow({flags: {foo: {type: 'string'}}})
13+
);
14+
expectAssignable<{flags: {foo: boolean}}>(
15+
meow({flags: {foo: {type: 'boolean'}}})
16+
);
17+
expectType<Result<never>>(meow({description: 'foo'}));
18+
expectType<Result<never>>(meow({description: false}));
19+
expectType<Result<never>>(meow({help: 'foo'}));
20+
expectType<Result<never>>(meow({help: false}));
21+
expectType<Result<never>>(meow({version: 'foo'}));
22+
expectType<Result<never>>(meow({version: false}));
23+
expectType<Result<never>>(meow({autoHelp: false}));
24+
expectType<Result<never>>(meow({autoVersion: false}));
25+
expectType<Result<never>>(meow({pkg: {foo: 'bar'}}));
26+
expectType<Result<never>>(meow({argv: ['foo', 'bar']}));
27+
expectType<Result<never>>(meow({inferType: true}));
28+
expectType<Result<never>>(meow({booleanDefault: true}));
29+
expectType<Result<never>>(meow({booleanDefault: null}));
30+
expectType<Result<never>>(meow({booleanDefault: undefined}));
31+
expectType<Result<never>>(meow({hardRejection: false}));
3732

38-
const result = meow('Help text');
33+
const result = meow('Help text', {
34+
flags: {
35+
foo: {type: 'boolean', alias: 'f'},
36+
'foo-bar': {type: 'number'}
37+
}}
38+
);
3939

4040
expectType<string[]>(result.input);
41-
expectType<{[name: string]: unknown}>(result.flags);
42-
expectType<{[name: string]: unknown}>(result.unnormalizedFlags);
4341
expectType<PackageJson>(result.pkg);
4442
expectType<string>(result.help);
4543

44+
expectType<boolean>(result.flags.foo);
45+
expectType<boolean>(result.unnormalizedFlags.foo);
46+
expectType<unknown>(result.unnormalizedFlags.f);
47+
expectType<number>(result.unnormalizedFlags['foo-bar']);
48+
expectType<unknown>(result.flags.fooBar);
49+
4650
result.showHelp();
4751
result.showHelp(1);
4852
result.showVersion();

0 commit comments

Comments
 (0)