-
Notifications
You must be signed in to change notification settings - Fork 12.6k
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
Suggestion: Reopen static and instance side of classes #2957
Comments
Note comments in #563 |
Also worth linking #9 since it covers basically the same use cases |
yes, please! |
Need to write up the status of this one |
I like this suggestion, but would like to see it extended slightly. Say I have a class generated from a C# class. I would like to be able to apply decorators to properties of the class without having to change the generated code. I could see this working, if I am allowed to add an existing property to the partial class with the same type or a more general compatible type (so Example: // in Person.ts (a generated file from somewhere)
export class Person {
@key
id: number;
name: string;
}
// in PersonExtensions.ts
import { Person } from './Person';
partial class Person {
@required
name: any;
} |
+1 |
In JS you do this by extending the prototype. While a friendlier syntax would be welcome, supporting the traditional style would be great for those who need it. TS is rumored to be a superset of JS after all 🙂 // [ts] Property 'foo' does not exist on type 'Klass'.
Klass.prototype.foo = function() { console.log("foo!"); }
new Klass().foo(); |
I would like this, because for example, if a 3rd-party type definition is not accurate, I would like to augment the class in order to quickly fix it for my case. In my case, the constructor of a class is stated to receive an argument of one type, but in reality it can accept an argument of a union of two types, and I'd like to simply fix this without having to fork a library or rewrite the entire definition of the class. |
Is there any progression on this topic? I have the same problem as @trusktr . |
Random bikeshed as the original declare class Foo {};
interface class Foo {
staticMethod(): boolean;
};
let result = Foo.staticMethod(); // boolean |
Actually this already works: declare class Foo {
static abc(): void ;
}
declare namespace Foo {
export function bcd(): void;
}
Foo.abc();
Foo.bcd(); So maybe this should just be closed. |
I think this issue is also tracking adding new overloads to the construct signature, which can't be done with |
This idea would also be useful for extending builtins with private fields (to emulate internal slots) to prevent structural typing in builtins. e.g.: declare partial class ArrayBuffer {
#arrayBufferData: unknown;
}
const arrayBufferLike = {
byteLength: 200,
slice: (begin: number, end?: number) => new ArrayBuffer(20),
};
// Would now report an error as this doesn't actually work in practice
const dataView = new DataView(arrayBufferLike); |
So another problem today is not all interfaces even have declare var AbortSignal: {
prototype: AbortSignal;
new(): AbortSignal;
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/abort_static) */
abort(reason?: any): AbortSignal;
/** [MDN Reference](https://developer.mozilla.org/docs/Web/API/AbortSignal/timeout_static) */
timeout(milliseconds: number): AbortSignal;
}; which makes it impossible (without patching TypeScript) to add missing static methods in declarations as there is no // Subsequent variable declarations must have the same type. Variable 'AbortSignal' must be of type '{ new (): AbortSignal; prototype: AbortSignal; abort(reason?: any): AbortSignal; timeout(milliseconds: number): AbortSignal; }', but here has type '{ any(signals: readonly AbortSignal[]): AbortSignal; }'.ts(2403)
// lib.dom.d.ts(2337, 13): 'AbortSignal' was also declared here.
declare global {
var AbortSignal: {
any(signals: ReadonlyArray<AbortSignal>): AbortSignal;
};
} If everything were simply defined as declare global {
partial class AbortSignal {
static any(signals: ReadonlyArray<AbortSignal>): AbortSignal;
}
} |
This was seemingly possible for a while due to what amounted to a "bug" that was patched in TS 5.5. With this "bug" patched, it seems like now would be an excellent time to re-evaluate this. I imagine this has gone unlooked at for so long because it simply worked already. Example use from discord.js: https://github.com/discordjs/discord.js/blob/ba0cb66ff92b0c46b020a2e471501aa4432bc978/packages/discord.js/typings/index.d.ts#L246-L258 |
Summary
To support both the semantics of subclassing built-ins in ES6 and still allow authors to augment built-ins, we need a mechanism to reopen the static and instance sides of a class.
Current state
Today we can re-open interfaces, allowing authors to augment built-ins (for example, to support polyfills):
We can also re-open the static side of a class, in a limited fashion:
There are several issues with these approaches:
var
/interface
pattern in theextends
clause of a class in TypeScript, meaning that "classes" defined using this pattern cannot be subclassed in ES6, which is an issue for built-ins.module
, you can only use non-keyword identifiers for property names. So you could not, for example, add a[Symbol.species]
property to the class, or use decorators on these members.Proposal
I propose we add a new syntactic modifier for the
class
declaration that would indicate we are re-opening an existing class. For this example I am using the keywordpartial
, although the semantics here differ significantly than the same-named capability in C#:Rules
partial
class declaration must be preceded by a non-partial
class declaration in the same lexical scope. These should be the same rules that apply when merging a module with a class or function today.partial
class declaration must have the same module visibility as the preceding non-partial
class declaration.partial
class declaration must have the same generic type parameters (including constraints) as the non-partial
class declaration.partial
class declaration cannot have anextends
clause, but may have animplements
clause.partial
class declaration cannot have aconstructor
member.partial
class declaration cannot have members with the same name as existing members on a class.partial
class declaration cannot have initializers.partial
class declaration can have a class decorator. User code that executes in-between the initial class declaration and the partial declaration will be able to observe the class before decorators on thepartial
class are applied.partial
class.Out of scope
Previous discussions
This has also been discussed previously:
The text was updated successfully, but these errors were encountered: