Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve flags types to acknowledge isMultiple and isRequired options #154

Merged
merged 6 commits into from Aug 10, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
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
}
}