Skip to content

Anonymous type parameters #48594

Open
Open
@lirannl

Description

@lirannl

Suggestion

πŸ” Search Terms

anonymous, type parameter, type parameters

βœ… Viability Checklist

My suggestion meets these guidelines:

  • [*] This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • [*] This wouldn't change the runtime behavior of existing JavaScript code
  • [*] This could be implemented without emitting different JS based on the types of the expressions
  • [*] This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, new syntax sugar for JS, etc.)
  • [*] This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

Whenever one wants to apply a generic type constraint, a full type must be provided.

The suggestion is that in a type/function definition Typescript could accept a generic type constraint for the property/argument, without having to declare a type parameter

πŸ“ƒ Motivating Example

interface Person<Profession extends Carpenter | Hacker | Judge> {
  name: Exclude<extends string, "">;
  profession: Profession;
  employer?: Profession extends Hacker | Judge ? Person : never
}

const divide = (a: number, b: Exclude<extends number, 0>) => a/b;

const ping = (ip: extends `${number}.${number}.${number}.${number}` ? string : never) => {}

const definitelyReturnsTrue = (arg: Exclude<,undefined | null | false | "" | 0>) => !!arg;

πŸ’» Use Cases

When in a type/function definition, if wanting to use a generic constraint for a parameter/argument, one must declare a type parameter even though it doesn't get used anywhere else in the definition.

Personally I see myself using such a feature most when I'm writing complex functions that already have multiple type parameters declared, and while I really want to constrain a certain parameter (my most common use case is excluding "" from string arguments but I've had other uses too), I might forgo doing so, because adding another type parameter to the function significantly increases its' complexity (as I now have to mentally keep track of another parameter, which ends up only being used once).

The way I see it, adding a type parameter in the beginning of the function's definition means that parameter is meant to be used in several places within that function, like the Person interface's Profession example. If it's only used once, I generally shouldn't need a type parameter.

In the meantime, of course, I either add an extra type parameter which I'll only ever use once, or if I decide it's not worth the complexity, I give up on the desired type safety and go with the strictest non-generic constraint available to me (usually a primitive like string or number).

To be clear, I do assume that if using the function/interface's type, rather than calling it/constraining a new object literal, it will count as generic and require type parameters, as the language obviously can't infer the generic constraint's type parameter in that case. Typescript could maybe name the anonymous type parameter based on the argument/parameter it's used in?

Activity

whzx5byb

whzx5byb commented on Apr 7, 2022

@whzx5byb

A literal type constraint for ping could be defined by writing:
const ping = (ip: `${number}.${number}.${number}.${number}`) => {}

And for other "exclude" examples you need to wait for the negated type:
const divide = (a: number, b: number & not 0) => a/b

lirannl

lirannl commented on Apr 7, 2022

@lirannl
Author

Those are two interesting solutions, and while I will keep those in mind (especially the negated types) - this could still be useful for other things beyond Exclude<>, like a Partial<>, or ReturnType<extends (...args: unknown[]) => unknown>, so I don't believe these completely cover the usefulness of this suggestion

jcalz

jcalz commented on Apr 7, 2022

@jcalz
Contributor

If the compiler automatically and silently augments the list of type parameters in some scope, don't you still have to "mentally keep track of another parameter" except now you have to deduce where it is?

In your proposal, isn't Person<Judge> an invalid type and you need to write something like Person<Judge, "J. Reinhold"> in order for it to compile?

Or if not, then what type is Person<Judge>? Is it Person<Judge, string> which would defeat the purpose of the type (since Exclude<string, ""> is just string and points back to negated types as the actually desired feature here), or is it something else? And if so, what?

RyanCavanaugh

RyanCavanaugh commented on Apr 7, 2022

@RyanCavanaugh
Member

It seems like this is just trying to shoehorn in negated types, and has the same problems without solving any other use case. If a type parameter is anonymous then you can only refer to it once, which runs afoul of the golden rule of generics.

lirannl

lirannl commented on May 14, 2022

@lirannl
Author

If the compiler automatically and silently augments the list of type parameters in some scope, don't you still have to "mentally keep track of another parameter" except now you have to deduce where it is?

As far as I'm concerned, generally not. In the cases where I do have to keep track of it, it'll already be a regular, non-anonymous type parameter. As for deducing where it is - anonymous type parameters would only be usable in function parameters, or in interface/object type members, when you try to constrain a parameter to Person, you'll be prompted for both the Profession parameter, and a Name parameter - named after the property, and when you're prompted with name you'll go "Oh, I didn't declare that parameter! Must be an anonymous, let's use an anonymous" (which would effectively forward the parameter).

In your proposal, isn't Person<Judge> an invalid type and you need to write something like Person<Judge, "J. Reinhold"> in order for it to compile?

That is a good point. Yeah, it would be, but more importantly, having to specify the anonymous parameter every time would render the feature pointless. The only time this would be useful is if you pass an object literal/existing instance to a function accepting "Person", and even then in that function's signature, it's going to have to be specified as either another parameter, or as another anonymous parameter (so Person<Judge, >).
It might still be useful but I see how at that point at least I personally might as well just use negated types once they're implemented. I don't think it's completely useless but it's a big limitation and thanks for pointing that out.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    In DiscussionNot yet reached consensusSuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

      Development

      No branches or pull requests

        Participants

        @jcalz@DanielRosenwasser@lirannl@whzx5byb@RyanCavanaugh

        Issue actions

          Anonymous type parameters Β· Issue #48594 Β· microsoft/TypeScript