Skip to content

Commit

Permalink
Improve flags types to acknowledge isMultiple and isRequired opti…
Browse files Browse the repository at this point in the history
…ons (#154)

Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
  • Loading branch information
voxpelli and sindresorhus committed Aug 10, 2020
1 parent fa2a374 commit e38789f
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 11 deletions.
23 changes: 18 additions & 5 deletions index.d.ts
Expand Up @@ -25,7 +25,8 @@ declare namespace meow {
type BooleanFlag = Flag<'boolean', boolean>;
type NumberFlag = Flag<'number', number>;

type AnyFlags = {[key: string]: StringFlag | BooleanFlag | NumberFlag};
type AnyFlag = StringFlag | BooleanFlag | NumberFlag;
type AnyFlags = {[key: string]: AnyFlag};

interface Options<Flags extends AnyFlags> {
/**
Expand Down Expand Up @@ -195,14 +196,26 @@ declare namespace meow {
readonly hardRejection?: boolean;
}

type TypedFlags<Flags extends AnyFlags> = {
[F in keyof Flags]: Flags[F] extends {type: 'number'}
type TypedFlag<Flag extends AnyFlag> =
Flag extends {type: 'number'}
? number
: Flags[F] extends {type: 'string'}
: Flag extends {type: 'string'}
? string
: Flags[F] extends {type: 'boolean'}
: Flag extends {type: 'boolean'}
? boolean
: unknown;

type PossiblyOptionalFlag<Flag extends AnyFlag, FlagType> =
Flag extends {isRequired: true}
? FlagType
: Flag extends {default: any}
? FlagType
: FlagType | undefined;

type TypedFlags<Flags extends AnyFlags> = {
[F in keyof Flags]: Flags[F] extends {isMultiple: true}
? PossiblyOptionalFlag<Flags[F], Array<TypedFlag<Flags[F]>>>
: PossiblyOptionalFlag<Flags[F], TypedFlag<Flags[F]>>
};

interface Result<Flags extends AnyFlags> {
Expand Down
24 changes: 18 additions & 6 deletions index.test-d.ts
Expand Up @@ -6,12 +6,21 @@ import {Result} from '.';
expectType<Result<never>>(meow('Help text'));
expectType<Result<never>>(meow('Help text', {hardRejection: false}));
expectAssignable<{flags: {foo: number}}>(
meow({flags: {foo: {type: 'number'}}})
meow({flags: {foo: {type: 'number', isRequired: true}}})
);
expectAssignable<{flags: {foo: string}}>(
meow({flags: {foo: {type: 'string'}}})
meow({flags: {foo: {type: 'string', isRequired: true}}})
);
expectAssignable<{flags: {foo: boolean}}>(
meow({flags: {foo: {type: 'boolean', isRequired: true}}})
);
expectAssignable<{flags: {foo: number | undefined}}>(
meow({flags: {foo: {type: 'number'}}})
);
expectAssignable<{flags: {foo: string | undefined}}>(
meow({flags: {foo: {type: 'string'}}})
);
expectAssignable<{flags: {foo: boolean | undefined}}>(
meow({flags: {foo: {type: 'boolean'}}})
);
expectType<Result<never>>(meow({description: 'foo'}));
Expand All @@ -34,21 +43,24 @@ const result = meow('Help text', {
flags: {
foo: {type: 'boolean', alias: 'f'},
'foo-bar': {type: 'number'},
bar: {type: 'string', default: ''}
bar: {type: 'string', default: ''},
abc: {type: 'string', isMultiple: true}
}
});

expectType<string[]>(result.input);
expectType<PackageJson>(result.pkg);
expectType<string>(result.help);

expectType<boolean>(result.flags.foo);
expectType<boolean | undefined>(result.flags.foo);
expectType<unknown>(result.flags.fooBar);
expectType<string>(result.flags.bar);
expectType<boolean>(result.unnormalizedFlags.foo);
expectType<string[] | undefined>(result.flags.abc);
expectType<boolean | undefined>(result.unnormalizedFlags.foo);
expectType<unknown>(result.unnormalizedFlags.f);
expectType<number>(result.unnormalizedFlags['foo-bar']);
expectType<number | undefined>(result.unnormalizedFlags['foo-bar']);
expectType<string>(result.unnormalizedFlags.bar);
expectType<string[] | undefined>(result.unnormalizedFlags.abc);

result.showHelp();
result.showHelp(1);
Expand Down
11 changes: 11 additions & 0 deletions test/test.js
Expand Up @@ -510,6 +510,17 @@ test('isMultiple - handles multi-word flag name', t => {
});
});

test('isMultiple - handles non-set flags correctly', t => {
t.deepEqual(meow({
argv: [],
flags: {
foo: {
isMultiple: true
}
}
}).flags, {});
});

if (NODE_MAJOR_VERSION >= 14) {
test('supports es modules', async t => {
try {
Expand Down
19 changes: 19 additions & 0 deletions tsconfig.json
@@ -0,0 +1,19 @@
{
"files": [
"index.d.ts",
"index.test-d.ts",
],
"compilerOptions": {
"strict": true,
"jsx": "react",
"target": "es2018",
"lib": [
"es2018"
],
"module": "commonjs",
"moduleResolution": "node",
"noImplicitAny": true,
"strictNullChecks": true,
"noUnusedLocals": true
}
}

0 comments on commit e38789f

Please sign in to comment.