Skip to content

Commit

Permalink
Fix TypeScript types (#245)
Browse files Browse the repository at this point in the history
  • Loading branch information
tommy-mitchell committed Aug 27, 2023
1 parent b412c0d commit 8a4cecd
Show file tree
Hide file tree
Showing 6 changed files with 162 additions and 10 deletions.
11 changes: 6 additions & 5 deletions package.json
Expand Up @@ -12,7 +12,7 @@
},
"type": "module",
"exports": {
"types": "./index.d.ts",
"types": "./build/index.d.ts",
"default": "./build/index.js"
},
"sideEffects": false,
Expand All @@ -22,11 +22,10 @@
"scripts": {
"prepare": "npm run build",
"build": "rollup --config",
"test": "xo && tsd && npm run build && ava"
"test": "xo && npm run build && ava && tsd --typings build/index.d.ts"
},
"files": [
"build",
"index.d.ts"
"build"
],
"keywords": [
"cli",
Expand Down Expand Up @@ -82,10 +81,12 @@
"read-pkg-up": "^10.0.0",
"redent": "^4.0.0",
"rollup": "^3.27.0",
"rollup-plugin-dts": "^6.0.0",
"rollup-plugin-license": "^3.0.1",
"trim-newlines": "^5.0.0",
"tsd": "^0.28.1",
"type-fest": "^4.2.0",
"type-fest": "^4.3.1",
"typescript": "~5.1.6",
"xo": "^0.56.0",
"yargs-parser": "^21.1.1"
},
Expand Down
19 changes: 18 additions & 1 deletion rollup.config.js
Expand Up @@ -2,6 +2,7 @@ import {nodeResolve} from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import json from '@rollup/plugin-json';
import license from 'rollup-plugin-license';
import {dts} from 'rollup-plugin-dts';
import {globby} from 'globby';
import {createTag, replaceResultTransformer} from 'common-tags';
import {delete_comments as deleteComments} from 'delete_comments';
Expand All @@ -17,7 +18,7 @@ const stripComments = createTag(

const outputDirectory = 'build';

export default defineConfig({
const config = defineConfig({
input: await globby('source/**/*.js'),
output: {
dir: outputDirectory,
Expand Down Expand Up @@ -55,3 +56,19 @@ export default defineConfig({
}),
],
});

const dtsConfig = defineConfig({
input: './source/index.d.ts',
output: {
file: `./${outputDirectory}/index.d.ts`,
format: 'es',
},
plugins: [
dts({
respectExternal: true,
}),
],
});

// eslint-disable-next-line import/no-anonymous-default-export
export default [config, dtsConfig];
File renamed without changes.
132 changes: 132 additions & 0 deletions test-d/build.test-d.ts
@@ -0,0 +1,132 @@
import {expectAssignable, expectError, expectType} from 'tsd';
import type {PackageJson} from 'type-fest';
import meow, {type Result} from '../build/index.js';

type AnyFlag = NonNullable<NonNullable<Parameters<typeof meow>[0]>['flags']>[string];

const importMeta = import.meta;

expectType<Result<never>>(meow('Help text'));
expectType<Result<never>>(meow('Help text', {importMeta, hardRejection: false}));
expectAssignable<{flags: {foo: number}}>(
meow({importMeta: import.meta, flags: {foo: {type: 'number', isRequired: true}}}),
);
expectAssignable<{flags: {foo: string}}>(
meow({importMeta, flags: {foo: {type: 'string', isRequired: true}}}),
);
expectAssignable<{flags: {foo: boolean}}>(
meow({importMeta, flags: {foo: {type: 'boolean', isRequired: true}}}),
);
expectAssignable<{flags: {foo: number | undefined}}>(
meow({importMeta, flags: {foo: {type: 'number'}}}),
);
expectAssignable<{flags: {foo: string | undefined}}>(
meow({importMeta, flags: {foo: {type: 'string'}}}),
);
expectAssignable<{flags: {foo: boolean | undefined}}>(
meow({importMeta, flags: {foo: {type: 'boolean'}}}),
);
expectAssignable<{flags: {foo: number[] | undefined}}>(
meow({importMeta, flags: {foo: {type: 'number', isMultiple: true}}}),
);
expectAssignable<{flags: {foo: string[] | undefined}}>(
meow({importMeta, flags: {foo: {type: 'string', isMultiple: true}}}),
);
expectAssignable<{flags: {foo: boolean[] | undefined}}>(
meow({importMeta, flags: {foo: {type: 'boolean', isMultiple: true}}}),
);
expectType<Result<never>>(meow({importMeta, description: 'foo'}));
expectType<Result<never>>(meow({importMeta, description: false}));
expectType<Result<never>>(meow({importMeta, help: 'foo'}));
expectType<Result<never>>(meow({importMeta, help: false}));
expectType<Result<never>>(meow({importMeta, version: 'foo'}));
expectType<Result<never>>(meow({importMeta, version: false}));
expectType<Result<never>>(meow({importMeta, autoHelp: false}));
expectType<Result<never>>(meow({importMeta, autoVersion: false}));
expectType<Result<never>>(meow({importMeta, pkg: {foo: 'bar'}}));
expectType<Result<never>>(meow({importMeta, argv: ['foo', 'bar']}));
expectType<Result<never>>(meow({importMeta, inferType: true}));
expectType<Result<never>>(meow({importMeta, booleanDefault: true}));
expectType<Result<never>>(meow({importMeta, booleanDefault: null}));
expectType<Result<never>>(meow({importMeta, booleanDefault: undefined}));
expectType<Result<never>>(meow({importMeta, hardRejection: false}));

const result = meow('Help text', {
importMeta,
flags: {
foo: {type: 'boolean', shortFlag: 'f'},
'foo-bar': {type: 'number', aliases: ['foobar', 'fooBar']},
bar: {type: 'string', default: ''},
abc: {type: 'string', isMultiple: true},
baz: {type: 'string', choices: ['rainbow', 'cat', 'unicorn']},
},
});

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

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

result.showHelp();
result.showHelp(1);
result.showVersion();

const options = {
importMeta,
flags: {
rainbow: {
type: 'boolean',
shortFlag: 'r',
},
},
} as const;

meow('', options);

expectAssignable<AnyFlag>({type: 'string', default: 'cat'});
expectAssignable<AnyFlag>({type: 'number', default: 42});
expectAssignable<AnyFlag>({type: 'boolean', default: true});

expectAssignable<AnyFlag>({type: 'string', default: undefined});
expectAssignable<AnyFlag>({type: 'number', default: undefined});
expectAssignable<AnyFlag>({type: 'boolean', default: undefined});

expectAssignable<AnyFlag>({type: 'string', isMultiple: true, default: ['cat']});
expectAssignable<AnyFlag>({type: 'number', isMultiple: true, default: [42]});
expectAssignable<AnyFlag>({type: 'boolean', isMultiple: true, default: [false]});

expectError<AnyFlag>({type: 'string', isMultiple: true, default: 'cat'});
expectError<AnyFlag>({type: 'number', isMultiple: true, default: 42});
expectError<AnyFlag>({type: 'boolean', isMultiple: true, default: false});

expectAssignable<AnyFlag>({type: 'string', choices: ['cat', 'unicorn']});
expectAssignable<AnyFlag>({type: 'number', choices: [1, 2]});
expectAssignable<AnyFlag>({type: 'boolean', choices: [true, false]});
expectAssignable<AnyFlag>({type: 'string', isMultiple: true, choices: ['cat']});
expectAssignable<AnyFlag>({type: 'string', isMultiple: false, choices: ['cat']});

expectError<AnyFlag>({type: 'string', choices: 'cat'});
expectError<AnyFlag>({type: 'number', choices: 1});
expectError<AnyFlag>({type: 'boolean', choices: true});

expectError<AnyFlag>({type: 'string', choices: [1]});
expectError<AnyFlag>({type: 'number', choices: ['cat']});
expectError<AnyFlag>({type: 'boolean', choices: ['cat']});

expectAssignable<AnyFlag>({choices: ['cat']});
expectAssignable<AnyFlag>({choices: [1]});
expectAssignable<AnyFlag>({choices: [true]});
expectError<AnyFlag>({choices: ['cat', 1, true]});
4 changes: 3 additions & 1 deletion index.test-d.ts → test-d/index.test-d.ts
@@ -1,6 +1,8 @@
import {expectAssignable, expectError, expectType} from 'tsd';
import type {PackageJson} from 'type-fest';
import meow, {type Result, type AnyFlag} from './index.js';
import meow, {type Result} from '../source/index.js';

type AnyFlag = NonNullable<NonNullable<Parameters<typeof meow>[0]>['flags']>[string];

const importMeta = import.meta;

Expand Down
6 changes: 3 additions & 3 deletions tsconfig.json
@@ -1,7 +1,7 @@
{
"files": [
"index.d.ts",
"index.test-d.ts",
"include": [
"source/index.d.ts",
"test-d",
],
"compilerOptions": {
"strict": true,
Expand Down

0 comments on commit 8a4cecd

Please sign in to comment.