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

fix(yargs): type 'string' is not assignable to type '"string"' #28061

Closed
wants to merge 1 commit into from

Conversation

cnishina
Copy link
Contributor

Reasoning for change. There is logic built into the interface Options where the property of types must be either the value of "string", "number" , etc. However, the type of the input is just a string. Having the logic built in makes it unusable.

  • Use a meaningful title for the pull request. Include the name of the package modified.
  • Test the change in your own code. (Compile and run.)
  • Add or edit tests to reflect the change. (Run with npm test.)
  • Follow the advice from the readme.
  • Avoid common mistakes.
  • Run npm run lint package-name (or tsc if no tslint.json is present).
  • Increase the version number in the header if appropriate.

Reasoning for change. There is logic built into the interface Options
where the property of types must be either the value of "string",
"number" , etc. However, the type of the input is just a string. Having
the logic built in makes it unusable.
@cnishina
Copy link
Contributor Author

In my project, I have the following error in my CircleCI:

Transpile errors due to

lib/cli/index.ts:62:38 - error TS2345: Argument of type '{ describe: string; default: string; type: string; }' is not assignable to parameter of type 'Options'.
  Types of property 'type' are incompatible.
    Type 'string' is not assignable to type '"string" | "number" | "boolean" | "array" | "count"'.

62       return yargs.option('out_dir', outDir)
                                        ~~~~~~

When applying the fix in the PR to the local node_modules/@types/yargs, the error disappears.

@typescript-bot
Copy link
Contributor

typescript-bot commented Aug 12, 2018

@cnishina Thank you for submitting this PR!

🔔 @poelstra @mizunashi-mana @pushplay @jeffkenney @JimiC - please review this PR in the next few days. Be sure to explicitly select Approve or Request Changes in the GitHub UI so I know what's going on.

If no reviewer appears after a week, a DefinitelyTyped maintainer will review the PR instead.

@typescript-bot typescript-bot added Popular package This PR affects a popular package (as counted by NPM download counts). Awaiting reviewer feedback labels Aug 12, 2018
@JimiC
Copy link
Contributor

JimiC commented Aug 12, 2018

@cnishina I'm afraid this is the wrong approach. The definition for option is option(key: string, options: Options): Argv; . Your outDir should be an Options type and not a string type.

In case outDir is indeed an Options type, the type declaration needs to be literally the word 'string';

@cnishina
Copy link
Contributor Author

It does follow the Options interface.

const outDir = {
  describe: 'Location of output.',
  default: 'downloads',
  type: 'string'
};

type: 'string' does not work.

Your comment above says my outDir should be an Option and I believe it is. In my use of the options method, I pass a string and an object.

@cnishina
Copy link
Contributor Author

cnishina commented Aug 12, 2018

type is defined as: "array" | "count" | PositionalOptionsType

And the PositionalOptionsType is defined as: type PositionalOptionsType = "boolean" | "number" | "string";

If the value of type can be an "array", "count", "boolean", "number", or "string" then this should be built into the yargs node module logic. Those values are still of type string.

However, if they actually can take on those types, then it should be:

Array<any> | number | boolean | string

@JimiC
Copy link
Contributor

JimiC commented Aug 12, 2018

Have a look at the official docs. type needs to be literal.
And we did so in the declaration so we constrain what literal strings are used.

@JimiC
Copy link
Contributor

JimiC commented Aug 12, 2018

I do see that using

const outDir = {
  default: 'downloads',
  describe: 'Location of output.',
  type: 'string',
};
yargs.option('out_dir', outDir);

complains but

yargs.option('out_dir', {
  default: 'downloads',
  describe: 'Location of output.',
  type: 'string',
});

is more than fine.

I'll investigate further why the definition complains.

@JimiC
Copy link
Contributor

JimiC commented Aug 12, 2018

Now when I use

const outDir: yargs.Options = {
  default: 'downloads',
  describe: 'Location of output.',
  type: 'string',
};
yargs.option('out_dir', outDir);

the complain goes away.

@typescript-bot typescript-bot added Unowned This PR touches a package that doesn't have any listed owners. and removed Awaiting reviewer feedback labels Aug 12, 2018
@typescript-bot typescript-bot added Awaiting reviewer feedback and removed Unowned This PR touches a package that doesn't have any listed owners. labels Aug 12, 2018
@typescript-bot typescript-bot added Unowned This PR touches a package that doesn't have any listed owners. and removed Awaiting reviewer feedback labels Aug 12, 2018
@cnishina
Copy link
Contributor Author

New error with yargs.Options

It works for outDir. Here is the new error:

const chrome: yargs.Options = {
  describe: 'Install or update chromedriver.',
  default: true,
  type: 'boolean'
};


Type '{ describe: string; defualt: boolean; type: "boolean"; }'
 Is not assignable to type 'Options'.
  Types of property 'type' are incompatible.
   Type '"boolean"' is not assignable to type '"string"'.

Should we cast into yargs.Options just to get an interface to work?

An interface is just an object and you should not have to cast it as yargs.Options. It appears to go away by casting it into yarg.Options but that's probably not a good thing. When calling .option I should not have to do:

.option('foo', { describe: 'foo describe', default: 'bar', type: 'string'}  as yargs.Options)

I really just want to do:

.option('foo', { describe: 'foo describe', default: 'bar', type: 'string'})

@cnishina
Copy link
Contributor Author

@JimiC @andy-ms Please see comment above.

@JimiC
Copy link
Contributor

JimiC commented Aug 12, 2018

.option('foo', { describe: 'foo describe', default: 'bar', type: 'string'}) works fine as I've already mentioned in #28061 (comment).

@cnishina
Copy link
Contributor Author

Again please review the part about the boolean.

@JimiC
Copy link
Contributor

JimiC commented Aug 12, 2018

const chrome: yargs.Options = {
  describe: 'Install or update chromedriver.',
  default: true,
  type: 'boolean'
};
yargs.option('driver', chrome);

also works fine.

@JimiC
Copy link
Contributor

JimiC commented Aug 12, 2018

Maybe it's best to use .options. Have a look at how I use it here.

@cnishina
Copy link
Contributor Author

Well I'm not sure. Let's see, I'm running:

[email protected]
[email protected]
@types/[email protected]

This fails on my computer and circleci.

https://circleci.com/gh/cnishina/webdriver-manager-replacement/128

@cnishina
Copy link
Contributor Author

cnishina commented Aug 12, 2018

error

Here is me adding the object into the .option method. Again if it is an object described by an interface, I should be able to create it separately. I have also experimented on this and changed the declaration to let instead of a const and that also does not work.

Is your opinion here that this is only my issue and should not be merged? This feels like a lot of push back for my issue that I've investigated and also described.

@cnishina
Copy link
Contributor Author

I don't think this is right. It should be typed strongly but we should let the yargs package decide what a valid input is for the second argument of yargs.Options. Strongly typed should help the developer write TypeScript. This error again is not obvious. The interface is just an object and when declared, it should not have to be casted just to get TypeScript to work.

@JimiC
Copy link
Contributor

JimiC commented Aug 12, 2018

It does because typescript does an internal type check.

@JimiC
Copy link
Contributor

JimiC commented Aug 12, 2018

Let me explain further.
This

const test = {
  describe: 'A test case.',
  default: {},
  type: 'object'
};

is a valid variable declaration for typescript, but it's an invalid Options type for yargs.

Now if you write it as

const test: yargs.Options = {
  describe: 'A test case.',
  default: {},
  type: 'object'
};

typescript immediately infers that it's not a valid Options type, and warns you ahead. And it's not valid because there can't be a type: 'object', as it's rescricted by the typings.

Your change, on the other hand, will allow this declaration and it's not valid according to the official yargs docs.

I hope I made myself clear and why your PR is invalid.

@cnishina
Copy link
Contributor Author

Again, comment above is that interfaces are just objects. When writing out this interface, if I satisfy that the type of type is just a string, then it satisfies the type definition. After it is transpiled, when executing the project, yargs would throw an error during runtime. You are preventing a runtime error with logic in your typings.

This is not productive and I think we need another person to weigh in. @andy-ms. Thoughts?

@JimiC
Copy link
Contributor

JimiC commented Aug 12, 2018

@andy-ms is not a member of the authoring team.

Copy link
Contributor

@JimiC JimiC left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR is invalid.

@cnishina
Copy link
Contributor Author

I spoke too soon that it is a runtime error. There is no runtime error so maybe the logic in typings is fine until we fix yargs.

yargs/yargs#1198

@typescript-bot typescript-bot added Revision needed This PR needs code changes before it can be merged. and removed Awaiting reviewer feedback labels Aug 12, 2018
@typescript-bot
Copy link
Contributor

@cnishina One or more reviewers has requested changes. Please address their comments. I'll be back once they sign off or you've pushed new commits or comments. Thank you!

@cnishina
Copy link
Contributor Author

Is the revision to close the PR? I think this is the wrong label.

@typescript-bot typescript-bot added the Revision needed This PR needs code changes before it can be merged. label Aug 12, 2018
@cnishina cnishina closed this Aug 13, 2018
@cnishina cnishina reopened this Aug 13, 2018
@cnishina
Copy link
Contributor Author

cnishina commented Aug 13, 2018

One last thing:

yargs.option('them_bananas', { type: 'bananas' })

is valid. There's nothing wrong with this... yargs just figures out what type it is when the option is used.

@typescript-bot typescript-bot added the Revision needed This PR needs code changes before it can be merged. label Aug 13, 2018
cnishina added a commit to cnishina/webdriver-manager-replacement that referenced this pull request Aug 13, 2018
- Do not use yarg typings until
DefinitelyTyped/DefinitelyTyped#28061 is
resolved.
- Created e2e test to updates, checks status, starts server, and removes
files.
- Expose seleniumProcess from the SeleniumServer class.
- Moved SIGINT event to start handler method. This will let a user stop
the selenium sever with the process pid and without existing their
JavaScript process.
cnishina added a commit to cnishina/webdriver-manager-replacement that referenced this pull request Aug 13, 2018
- Do not use yarg typings until
DefinitelyTyped/DefinitelyTyped#28061 is
resolved.
- Created e2e test to updates, checks status, starts server, and removes
files.
- Expose seleniumProcess from the SeleniumServer class.
- Moved SIGINT event to start handler method. This will let a user stop
the selenium sever with the process pid and without existing their
JavaScript process.
@typescript-bot typescript-bot added the Revision needed This PR needs code changes before it can be merged. label Aug 13, 2018
cnishina added a commit to cnishina/webdriver-manager-replacement that referenced this pull request Aug 13, 2018
- Do not use yarg typings until
DefinitelyTyped/DefinitelyTyped#28061 is
resolved.
- Created e2e test to updates, checks status, starts server, and removes
files.
- Expose seleniumProcess from the SeleniumServer class.
- Moved SIGINT event to start handler method. This will let a user stop
the selenium sever with the process pid and without existing their
JavaScript process.
cnishina added a commit to cnishina/webdriver-manager-replacement that referenced this pull request Aug 13, 2018
- Do not use yargs typings until
DefinitelyTyped/DefinitelyTyped#28061 is
resolved.
- Created e2e test to updates, checks status, starts server, and removes
files.
- Expose seleniumProcess from the SeleniumServer class.
- Moved SIGINT event to start handler method. This will let a user stop
the selenium sever with the process pid and without existing their
JavaScript process.
cnishina added a commit to cnishina/webdriver-manager-replacement that referenced this pull request Aug 13, 2018
- Do not use yargs typings until
DefinitelyTyped/DefinitelyTyped#28061 is
resolved.
- Created e2e test to updates, checks status, starts server, and removes
files.
- Expose seleniumProcess from the SeleniumServer class.
- Moved SIGINT event to start handler method. This will let a user stop
the selenium sever with the process pid and without existing their
JavaScript process.
cnishina added a commit to cnishina/webdriver-manager-replacement that referenced this pull request Aug 13, 2018
- Do not use yargs typings until
DefinitelyTyped/DefinitelyTyped#28061 is
resolved.
- Created e2e test to updates, checks status, starts server, and removes
files.
- Expose seleniumProcess from the SeleniumServer class.
- Moved SIGINT event to start handler method. This will let a user stop
the selenium sever with the process pid and without existing their
JavaScript process.

closes #24
@typescript-bot typescript-bot added the Revision needed This PR needs code changes before it can be merged. label Aug 13, 2018
@ghost
Copy link

ghost commented Aug 13, 2018

See microsoft/TypeScript#25889 -- string literals need a contextual type to avoid being turned into string.
It might be true that { type: "bananas" } doesn't crash, but neither does Math.abs(true) and we issue an error for that. See https://stackoverflow.com/questions/41750390/what-does-all-legal-javascript-is-legal-typescript-mean

@cnishina
Copy link
Contributor Author

Since this is my first project with yargs, I was not sure if I was using yargs wrong or not. For the most part, the yargs type definitions did help but things did not go well when I started using the option method. When I received the sort of vague error message, I created the PR to remove the string literals. It took several back and forth messages from the author to nail down that the object created would need to be cast into the interface and not how I was using it. #28061 (comment)

It looks like you have this: microsoft/TypeScript#26413 which will help debugging messages for string literals in interfaces. If the error message said I needed to cast into the yargs.Options then I would be fine.

In its current state, I am still not a fan with the error message but I will close the PR. Thank you @andy-ms for being referee / third party opinion.

Thank you @JimiC for debugging this with me.

@cnishina cnishina closed this Aug 13, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Popular package This PR affects a popular package (as counted by NPM download counts). Revision needed This PR needs code changes before it can be merged.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants