microsoft / TypeScript

TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
https://www.typescriptlang.org
Apache License 2.0
100.4k stars 12.41k forks source link

Allow `abstract` modifier on construct signature in object type literal #57171

Open parzhitsky opened 8 months ago

parzhitsky commented 8 months ago

šŸ” Search Terms

'abstract' modifier cannot appear on a type member

āœ… Viability Checklist

ā­ Suggestion

Allow using abstract new ā€¦ constructor definition keywords in an interface, i.e. in an object form (i.e., within curly braces).

šŸ“ƒ Motivating Example

Consider possible definitions of:

All of those definitions can be created through type aliasing:

type Callable = () => void // āœ…
type Constructor = new () => void // āœ…
type ConstructorAbstract = abstract new () => void // āœ…

But only the first two definitions can be created using an object form:

type Callable = { (): void } // āœ…
type Constructor = { new (): void } // āœ…
type ConstructorAbstract = { abstract new (): void } // āŒ
//                           ^^^^^^^^
// Error: 'abstract' modifier cannot appear on a type member

Try it.

Apparently, in this form the token new is considered a method name, rather than a keyword.

I suggest making the last one a valid expression by parsing new as a keyword.

šŸ’» Use Cases

This suggestion is mainly for visual consistency, there is no functional change sought to be. To the folks who prefer using interfaces whenever possible (like me), the requirement of using type here is somewhat unreasonable, although it doesn't pose any significant problems.

parzhitsky commented 8 months ago

This looks more like a bug, but there is no "previous behaviour that is broken", so I'm filing it as a suggestion

MartinJohns commented 8 months ago

Related: #41197

StepanMynarik commented 1 month ago

Same problem here.

I have special type for abstract components in my game engine (that is used to constrain whether abstract components can be passed to a certain function or not).

I also have other type for normal non-abstract components.

The second one can normally have the constructor defined in it:

export interface IComponentClass<TComponent extends Component> {
  new(entity: Entity): TComponent;
  prototype: TComponent;
  etc...
};
export type ComponentClass<TComponent extends Component> = IComponentClass<TComponent>;

While the first one not, so I solve it like this (it works but it will be confusing in the future, why both are not defined the same way):

export type AbstractComponentConstructor<TComponent extends Component> = abstract new(entity: Entity) => TComponent;
export interface IAbstractComponentClass<TComponent extends Component> {
  prototype: TComponent;
}
export type AbstractComponentClass<TComponent extends Component> = AbstractComponentConstructor<TComponent>
  & IAbstractComponentClass<TComponent>;