microsoft / microsoft/TypeScript

Allow overloading constructors with type parameters to instantiate the same class but with different generics

Open
#54,157 16 comments 16 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Awaiting More Feedback Suggestion
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
1d 19h
Merged PRs (30d)
117

Description

Suggestion

🔍 Search Terms

constructor overload return type parameter annotation generic

✅ 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.

Previous Discussions & Related Issues

  1. #10860
    The closest to a duplicate of this proposal. Closed due to conflicts between generics on class and generics on constructor overloads.

  2. #11588
    A feature request asking for TypeScript to allow setting any return type for a constructor. It was declined due to that pattern being considered bad practice.

  3. #27465
    Quickly closed due to being a duplicate of the first issue, #10860, did not end up with much further discussion once it was closed, unfortunately.

  4. #27594
    Asking again for TypeScript to allow setting the return type for a constructor. From what I can see, the issue does not specify whether the instance type should be assignable to the return type, or whether any return type should be allowed (IE, a straight rehash of #11588.) This issue is still open and has garnered a lot of discussion, but it's only tangentially related to this proposal.

⭐ Goals of this Proposal

1. Allow writing classes that generate the correct generics without requiring the consumer to provide any.

The ideal circumstances for class construction with generics is the following, where the consumer doesn't need to provide any type parameters:

// Ideal circumstances:
class Collection<T> {
  constructor(iterable: Iterable<T>) {}
}
new Collection([1, 2, 3]) // Collection<number>

That, unfortunately, is not always possible. In the following example, for instance, the ideal return would use HTMLElementTagNameMap[TAG_NAME], but there's no way to do that:

class ElementWrapper<TELEMENT extends Element = Element> {
  constructor(tagName: string) {}
}
new ElementWrapper("button") // ElementWrapper<Element>
new ElementWrapper<HTMLButtonElement>("button") // ElementWrapper<HTMLButtonElement>, but the consumer has to annotate it themself :(
2. Don't affect existing TypeScript code at all. Any new functionality should be layered over top what's already there.

When a developer isn't using any of the newly-allowed annotations, TypeScript should function exactly as it does now.

3. Don't introduce new syntax for providing type parameters to a constructor call.
4. Disallow constructors to be annotated as returning things not assignable to the class's type.

Returning anything is out of scope for this proposal.

🚀 Proposal

Constructor overload signatures can now have their own type parameters and provide a return type.

In order to allow this while still respecting the other stated goals above, this comes with the following restrictions:

  • When type parameters are specified, and the overload is called, the overload's type parameters are used instead of the class generics.

  • When a signature is using custom type parameters, it must, therefore, have a return type, otherwise there wouldn't be a way for TypeScript to determine the generics of new instances.

  • Any constructor return type must be assignable to the class's instance type. (As stated above, anything else is out of scope for this proposal.)

  • Again, only overloads may have type parameters or return types. The constructor implementation may not have type parameters or a return type, as it adds additional complexity to the proposal. The current errors will become Type parameters can only appear on overloaded constructor declarations. and Type annotations can only appear on overloaded constructor declarations.

Example

Using the above ElementWrapper example, we can now do the following:

class ElementWrapper<TELEMENT extends Element = Element> {
  constructor<TTAG_NAME extends keyof HTMLElementTagNameMap>
    (tagName: TTAG_NAME): ElementWrapper<HTMLElementTagNameMap[TTAG_NAME]>;
  constructor(tagName: string) {}
}

Constructor overloads can use different type parameters from each other.

The functionality exactly mirrors function overloads, and allows for the following:

class ElementWrapper<TELEMENT extends Element = Element> {
  constructor<TTAG_NAME extends keyof HTMLElementTagNameMap>
    (tagName: TTAG_NAME): ElementWrapper<HTMLElementTagNameMap[TTAG_NAME]>;
  constructor(tagName: string): ElementWrapper<Element>;
  constructor(tagName: string) {}
}

Constructor overloads should not be handled any differently in TypeScript than they are now (yes, it already works!), using the complex newable interface syntax. (To note, while that syntax works for type definitions, if a bit awkwardly, trying to implement the class itself using those types is even more awkward.)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

No implementation files, tests, or compiler entry points are named. Start by reviewing the proposal and related issues #10860, #11588, #27465, and #27594; done would require an agreed design and corresponding TypeScript behavior that supports generic constructor overloads without changing existing code.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
compilers
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.