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

[TS] Any way to predefine the flags then typecheck them in options? #129

Open
whitetrefoil opened this issue Dec 9, 2019 · 8 comments
Open

Comments

@whitetrefoil
Copy link

whitetrefoil commented Dec 9, 2019

Currently the type of flags are difficult to predefine, it'll be easier to write the meow(..., ...) function then get the returned type.

But I'm trying to predefine the flags' type then use it to typecheck the meow() function. e.g.:

// interfaces/config.ts

export interface IFlags {
  flag1: string;
  flag2: boolean;
  flag3: number;
}

export interface IConfig {
  argv: meow.IResult<IFlags>; // similar to meow.Result but pass in flag type not flag def
  some: string;
  other: boolean;
  configs: number[];
}
// config.ts

import {IFlags, IConfig} from './interfaces/config.ts';

const argv = anotherWayToMeow<IFlags>( // similar to current meow() but pass in flag type not def
  `Usage: xxxxxxx`,
  {
    flags: {
      // better to have typecheck here,
      // if not at least can predefine the result flags
      flag1: { alias: 'a', type: 'string' },
      flag2: { alias: 'b' }, // error because missing type: 'boolean'
      // error because missing flag3
    },
  },
)

const some = 'xxxx';
const other = process.env.MY_ENV === 'other';
const configs = [1,2,3];

const config: IConfig = { argv, some, other, configs };

export default config;
@sindresorhus
Copy link
Owner

Why do you need to predefine them? The Meow types are smart enough to use the correct types depending on what you specify in the flag.type property. See: 3e05a2e

// @NiGhTTraX

@whitetrefoil
Copy link
Author

That's because:

  1. I want to share the type of flags to other files.
  2. In my team, we're used to design a module/file (result in interfaces & type aliases) before coding it.
  3. Personally, I'm used to firstly write down the type I want then use it to type-check my impl. (this is the reason I use ts instead of js)

@NiGhTTraX
Copy link
Contributor

@whitetrefoil you don't need to pass a generic type to meow if you can define your expected result type:

type Flags = {
  foo: number;
}

const cli: { flags: Flags } = meow({
  flags: {
    foo: { type: 'number' }
  }
});

You would get a type error if you tried to use type: 'string' instead because the result wouldn't be assignable to your type.

@whitetrefoil
Copy link
Author

@NiGhTTraX That's a nice workaround. I'll use it before I find a better way (if there is). Thanks a lot!

@voxpelli
Copy link
Contributor

The solution suggested by @NiGhTTraX sounds like the proper one to me.

It's even the one used in meow's type tests:

meow/index.test-d.ts

Lines 8 to 16 in 629af48

expectAssignable<{flags: {foo: number}}>(
meow({flags: {foo: {type: 'number'}}})
);
expectAssignable<{flags: {foo: string}}>(
meow({flags: {foo: {type: 'string'}}})
);
expectAssignable<{flags: {foo: boolean}}>(
meow({flags: {foo: {type: 'boolean'}}})
);

@andrewmclagan
Copy link

andrewmclagan commented May 28, 2024

Would this not be solved by a very simple export of AnyFlags type? Type errors would be show at the same location as the flags definition, rather then where they are passed.

// file_1.ts
const flags: AnyFlags = {
  unicorn: {
    type: 'string',
  },
};

// file_2.ts
meow(flags);

@techtony92
Copy link

Ive tried the approach that was suggested by @NiGhTTraX but i got some errors(see example_type1 photo)
example_type_1
and we also loose type information on siblings. Typescript thinks Input and help don't exists even though they do.
I used ReturnType:
example_type_using_return_type
which gives all typings.
return_type_all_typings

@NiGhTTraX
Copy link
Contributor

@techtony92 you can use the satisfies operator from TypeScript 4.9+:

type Flags = { foo: string };

const x = meow({
  importMeta: import.meta,
  flags: {
    foo: { type: "number", isRequired: true },
  },
}) satisfies { flags: Flags };
// You get type checking here:
// TS1360: Type number is not assignable to type string

// You preserve the meow type here:
console.log(x.help);
//             ^? string
console.log(x.flags.foo);
//                   ^? number

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants