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

Allow implicitly typing class methods and constructor #54334

Closed
4 of 5 tasks
matthew-dean opened this issue May 20, 2023 · 4 comments
Closed
4 of 5 tasks

Allow implicitly typing class methods and constructor #54334

matthew-dean opened this issue May 20, 2023 · 4 comments
Labels
Duplicate An existing issue was already created

Comments

@matthew-dean
Copy link

matthew-dean commented May 20, 2023

Suggestion

In TypeScript's documentation of classes, it says:

In this example, we perhaps expected that s’s type would be influenced by the name: string parameter of check. It is not - implements clauses don’t change how the class body is checked or its type inferred.

I discovered while trying to type a .js class with @implements that while I thought the issue was with the JSDoc keyword, the issue is with implements itself. There is currently no way to implicitly type the methods of a class with a single definition. This would be IMO a great feature for TypeScript, especially as some libs move to lean more on JSDoc.

🔍 Search Terms

@implements implements

✅ 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

Allow implicit typing of the methods of a class from one place

📃 Motivating Example

So, take this example:

export interface Print {
  prototype: {
    print(txt: string): void;
  }
}

/** @class */
export const TextBook: Print = function() {}
TextBook.prototype.print = function(txt) {}

You'll note if you type-check this code that txt does not need explicit typing. However, AFAICT, there's no way to type a class this way in TypeScript. Meaning that classes actually have less TypeScript support (in this instance) then the traditional JavaScript pattern that class syntax often replaces.

In fact, as noted in the above documentation, TypeScript specifically does not provide this functionality even while noting that developers might expect this to be supported in some way (if only because the prototype syntax is supported).

In other words, there is no way to type the print method here using an external class, which makes type-checking a .js file with JSDoc more difficult than its .prototype. equivalent, or making a single type for a class nearly impossible. As in:

export const TextBook2: Print = class {
  print(txt) {}. // the type of `txt` will not be inferred
}

Note also that using .prototype. allows us to check for exact types and excess properties, whereas the class pattern offers no equivalent:
Screenshot 2023-05-20 at 1 42 35 PM

Again, this infers that TypeScript currently has no way to accurately describe the shape of a class externally, even though this seems to available for all other primitives and built-in objects.

💻 Use Cases

I'd like to be able to describe a class using a single JSDoc @type import, or, similarly, use a declaration like const Foo: MyClass = class {} to correctly type all properties, methods, and the constructor. This would maintain parity with what a developer can do right now in TypeScript with function prototypes.

Possible workaround

If, for some reason, there's some syntactic reasoning behind not fully supporting class types, it would be great if we could somehow tell TypeScript to support a type shape by describing it's constructor / prototype without actually using classic function prototypes. In other words, do what TypeScript can already do right now with Function.prototype, but apply it to the class syntax.

@jcalz
Copy link
Contributor

jcalz commented May 20, 2023

Looks like a duplicate of #32082 (presumably if implements clauses provided context for inference then your problem would be resolved?)

@matthew-dean
Copy link
Author

@jcalz I guess so. My motivating examples are a little different. That is, the discussion there seems focused on what classes should or shouldn't do based on the behavior of classes. My argument is what classes should do based on the behavior of every other type of primitive in TypeScript, with classes as the far outlier.

Also, I'm not proposing that the behavior of implements changes, so much that, if assigning an explicit type, the class object abides by that type. Currently, it doesn't. That to me is clearly a bug, or at least an aberration in the syntax.

@RyanCavanaugh RyanCavanaugh added the Duplicate An existing issue was already created label May 23, 2023
@RyanCavanaugh
Copy link
Member

I would consider this a duplicate of the linked issue as well. Currently, method declarations cannot be subject to contextual typing, and if we did allow that, then this would just all work as anticipated.

@microsoft-github-policy-service

This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Duplicate An existing issue was already created
Projects
None yet
Development

No branches or pull requests

3 participants