-
Notifications
You must be signed in to change notification settings - Fork 13.2k
Description
π Search Terms
class, expression
β Viability Checklist
- 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 isn't a request to add a new utility type: https://github.com/microsoft/TypeScript/wiki/No-New-Utility-Types
- This feature would agree with the rest of our Design Goals: https://github.com/Microsoft/TypeScript/wiki/TypeScript-Design-Goals
β Suggestion
A compiler option disallowClassExpressions or noClassExpressions
This is option would primarily be intended to strengthen language level understanding / error prevention. In addition it would help prevent errors hitting compiler "bugs"/limitations.
With the exception of actual class factories, there seems to be no practical use case for class expressions. It is merely an artifact of JS classes being syntax sugar for design patterns around functions, which in turn leads to (seemingly a lot of) confusion, errors and misuse.
A subset of issues that popped up because of class expressions, where there seems to be no obvious use case for leveraging that feature to solve a certain problem, but rather to get rid of an error message about declaration file capabilities; and on the contrary, all seem to show examples of "bad practice":
- False error TS4094: ... exported class expression may not be private or protected.Β #30355 (closed)
- Allow .d.ts files to represent anonymous class expressions with
privatemembersΒ #36060 (same, just reopened due to pushback) - Unexpected TS4094 with the build parameter
declaration: trueΒ #17293
This option would fit well among options like strict, noImplicitAny, noUncheckedIndexedAccess, and any other option that moves the compiler towards the stricter end of the type checking spectrum.
π Motivating Example
// These, and maybe more, have cleaner, less error prone and more capable alternatives,
// and seem to completely lack actual use cases among open or closed TS issues:
const foo = () => class Foo {}
const bar = class Bar {}
// Write this instead:
class Foo {}
class Bar {}
// For whatever use case, this then still achieves the same goal:
const foo = () => Foo;
const bar = Bar;
// Opting out of `disallowClassExpressions` via typing,
// something that tells the compiler that class assignment is expected.
// Maybe, maybe not. ts expect error might be safer than conflating expressions with assignability.
const foo: () => new () => any = () => class {};
const bar: new () => any = class {};π» Use Cases
-
What do you want to use this for?
Preventing runtime errors/overhead (TS design goal 1)
-
What shortcomings exist with current approaches?
Without this option, some authors tend to confuse "bad code" with compiler limitations, more often than actually hitting a compiler limitation/bug.
-
What workarounds are you using in the meantime?
manual maintenance, maybe custom linter rules.
Edit: Stumbled upon a situation that would render this option less useful than I initially imagined it to be. Being scoped to expressions, it would have no effect on declarations in closures (which should be in scope according to the motivating examples). Set aside that some already consider it to be out of scope, as stated, it wouldn't do what the name suggests (bad) or is less effective than it should be.