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

feat(flags): infer argument types, names and defaults #2180

Merged
merged 78 commits into from
Jun 21, 2022

Conversation

c4spar
Copy link
Contributor

@c4spar c4spar commented May 2, 2022

close #1898

@CLAassistant
Copy link

CLAassistant commented May 2, 2022

CLA assistant check
All committers have signed the CLA.

@c4spar c4spar changed the title feat(flags): infer argument types and defaults feat(flags): infer argument types, names and defaults May 2, 2022
flags/test.ts Outdated
import {
assert,
IsExact,
} from "https://deno.land/x/conditional_type_checks@1.0.5/mod.ts";
Copy link
Member

Choose a reason for hiding this comment

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

We prefer to avoid depending on deno.land/x/ from std. Can you copy the necessary part of the module in std as test utility?

Copy link
Contributor Author

@c4spar c4spar May 4, 2022

Choose a reason for hiding this comment

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

Yep i know, it was only temporarily 😉 will do 👍


/** ---------------------- TYPE TESTS ---------------------- */

Deno.test("typesOfAllBoolean", function (): void {
Copy link
Member

Choose a reason for hiding this comment

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

Nice test cases! These illustrate what's done in this change very well

@kt3k
Copy link
Member

kt3k commented May 4, 2022

@c4spar Can you fix the type error in CI?

error: TS2322 [ERROR]: Type 'unknown' is not assignable to type 'boolean | undefined'.
      showDirListing: serverArgs["dir-listing"],
      ~~~~~~~~~~~~~~
    at file:///Users/runner/work/deno_std/deno_std/http/file_server.ts:810:7

@c4spar
Copy link
Contributor Author

c4spar commented May 4, 2022

Oops, I assumed dashed parameters are camelcased 😅 is there a reason why we don't do that?

flags/mod.ts Outdated

/** Same as `Record` but also supports dotted and negatable options. */
type MapTypes<T extends ArgType, V> = undefined extends T ? Record<never, never>
: T extends `no-${infer Name}` ? MapTypes<Name, V | false>
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think the last thing that is missing is the handling of negatable options. How do we want to proceed with them? do we want to make them configurable by prefixing them with no- or with a separate option?

Copy link
Member

Choose a reason for hiding this comment

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

@c4spar
Sorry for the delay in response.

do we want to make them configurable by prefixing them with no- or with a separate option?

I don't think we should change it optional. That feels like an unnecessary breaking change to me. In my view --no-foo shorthand is always useful for boolean args. So I suggest we just keep the existing behavior

For negation of non-boolean args, I can't think of reasonable usages of it. So I'd suggest we should leave it undefined behavior and let's ignore it in typings. What do you think?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I still think we should keep support for negation of non boolean args.
A good example is deno run --no-config 😉 It's a string arg with a default behavior that can be disabled.

But if we don't make it configurable than all string and collectable args can be also false, which is a bit annoying.

Copy link
Member

Choose a reason for hiding this comment

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

Ok. --no-config looks like a useful example.

But if we don't make it configurable than all string and collectable args can be also false, which is a bit annoying.

I agree with this. What API do you suggest for configurable negatable args?

Is the following example what you suggest for configurable negatable args? (I think this is not implemented though)

    boolean: ["foo", "no-bar", "no-dotted.tick", "dotted.tock"],
    string: ["beep", "no-boop", "no-dotted.zig", "dotted.zag"],

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is the following example what you suggest for configurable negatable args? (I think this is not implemented though)

This would be the easiest solution and the typings for this are already implemented in this PR. I have also implemented the logic but only locally, i can open another PR for this.

Another solution would be to add a new negate option, same like the collect option. But the typings would be more complex for this solution.

Copy link
Member

Choose a reason for hiding this comment

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

@c4spar

This would be the easiest solution and the typings for this are already implemented in this PR. I have also implemented the logic but only locally, i can open another PR for this.

Thank you for your efforts on this, but this syntax (boolean: ["no-bar"]) for indicating negatable arg looks confusing/too implicit to me.

Another solution would be to add a new negate option, same like the collect option. But the typings would be more complex for this solution.

This solution looks much better to me as it's more explicit and probably easier to document. Also this feels consistent with collect option for configuring arg behaviors.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you for your efforts on this, but this syntax (boolean: ["no-bar"]) for indicating negatable arg looks confusing/too implicit to me.

I agree with you, I also don't really like it to activate --foo and --no-foo only with no-foo.

I would like it more if you could explicitly activate both separately like this string: ["foo", "no-foo"]. foo would add the type string and no-foo would add the type false. But this doesn't really work if only no-foo is defined, because foo is always a valid argument. A workaround could be to set foo to unknown if only no-foo is defined.

But i'm also fine with a new option to align with the collect option.

I tried both solutions already, but with both solutions I had some issues with union types, but I can give it a second try.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

ok i got the types for the negatable option working now 😉

flags/mod.ts Outdated

type CollectType = string | undefined;

type NoTypes<B, S, C> = undefined extends (
Copy link
Member

Choose a reason for hiding this comment

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

Instead of this being a negative name ("No"), maybe instead use a positive name ("UseTypes" maybe?).

flags/mod.ts Outdated

type CollectType = string | undefined;

type NoTypes<B, S, C> = undefined extends (
Copy link
Member

Choose a reason for hiding this comment

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

Maybe also add the type parameter constraints here to make it a little more clear what these type parameters could possibly be.

Though not necessary, it actually might be good to move away from single letter type parameter names and instead use longer more descriptive names because of how complex this is (I usually prefix type parameter names with a T to distinguish them from other types)

Copy link
Member

@kt3k kt3k left a comment

Choose a reason for hiding this comment

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

Great improvement of the usability of flags. Nice work!

LGTM

@kt3k
Copy link
Member

kt3k commented Jun 14, 2022

I think this PR is ready to land now.

@bartlomieju Do you have any concern about landing this?

flags/mod.ts Outdated

/** When `true`, populate the result `_` with everything after the first
* non-option. */
stopEarly?: boolean;

/** A string or array of strings argument names to always treat as strings. */
string?: string | string[];
string?: S | ReadonlyArray<Extract<S, string>>;

/** A string or array of strings argument names to always treat as arrays.
* Collectable options can be used multiple times. All values will be
* colelcted into one array. If a non-collectable option is used multiple
Copy link
Contributor

Choose a reason for hiding this comment

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

There's a typo here: colelcted to collected?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fixed. Thx @dcdunkan 👍

@bartlomieju
Copy link
Member

I think this PR is ready to land now.

@bartlomieju Do you have any concern about landing this?

No, this looks good to me!

@kt3k
Copy link
Member

kt3k commented Jun 21, 2022

Ok. Let's land now!

Thanks again for the series of works for landing this PR @c4spar!

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

Successfully merging this pull request may close these issues.

feature(flags): Add generic type to parse()
6 participants