microsoft / microsoft/TypeScript
declarations file of extended class type is needlessly verbose (excessively deep)
@weswigham is already working on this.
Since Jun 21, 2023.
- Dominant language
- Go
- Stars
- 111k
- Forks
- 14.4k
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 117
Description
Bug Report
When returning an extended class type from a function, the resulting .d.ts file is needlessly verbose resulting in very, very long .d.ts for chained extensions. (See playground for details).
🔎 Search Terms
emitDeclarations
polymorphic this
mixins
Base constructor must have same return type
Type instantiation is excessively deep and possibly infinite
🕗 Version & Regression Information
- Version v4.2.3 is the minimum version where the code can run without errors.
- All versions output this repeated structure
⏯ Playground Link
💻 Code
class CustomElement extends HTMLElement {
/**
* Useless constructor needed to workaround TS
* `Base constructors must all have the same return type.`
*/
constructor(...args: any) {
super();
}
/* Extends Class.prototype and returns new Class */
static define<
CLASS extends typeof CustomElement,
ARGS extends ConstructorParameters<CLASS>,
INSTANCE extends InstanceType<CLASS>,
PROPS extends object>
(this: CLASS, source: PROPS & ThisType<PROPS & INSTANCE>)
: CLASS & (new (...args: ARGS) => INSTANCE & PROPS) {
return Object.defineProperties(
this.prototype,
source as any,
) as any;
}
}
/** FooElement.js */
class FooElement extends CustomElement
.define({
/** Comment hover test */
definedInFoo1: true,
})
.define({
definedInFoo2: 'bar',
})
.define({
definedInFoo3: 3,
})
.define({
/** Sets 2 to 'foo' */
fooMethod() {
this.definedInFoo2 = 'foo';
}
}) { }
🙁 Actual behavior
Here's the outputed d.ts:
declare class CustomElement extends HTMLElement {
/**
* Useless constructor needed to workaround TS
* `Base constructors must all have the same return type.`
*/
constructor(...args: any);
static define<CLASS extends typeof CustomElement, ARGS extends ConstructorParameters<CLASS>, INSTANCE extends InstanceType<CLASS>, PROPS extends object>(this: CLASS, source: PROPS & ThisType<PROPS & INSTANCE>): CLASS & (new (...args: ARGS) => INSTANCE & PROPS);
}
declare const FooElement_base: typeof CustomElement & (new (...args: any) => CustomElement & {
/** Comment hover test */
definedInFoo1: boolean;
}) & (new (...args: any) => CustomElement & {
/** Comment hover test */
definedInFoo1: boolean;
} & {
definedInFoo2: string;
}) & (new (...args: any) => CustomElement & {
/** Comment hover test */
definedInFoo1: boolean;
} & {
definedInFoo2: string;
} & {
definedInFoo3: number;
}) & (new (...args: any) => CustomElement & {
/** Comment hover test */
definedInFoo1: boolean;
} & {
definedInFoo2: string;
} & {
definedInFoo3: number;
} & {
/** Sets 2 to 'foo' */
fooMethod(): void;
});
/** FooElement.js */
declare class FooElement extends FooElement_base {
}
🙂 Expected behavior
I expected FooElement_base to be shorter, smaller d.ts like this:
declare const FooElement_base: typeof CustomElement & (new (...args: any) => CustomElement & {
/** Comment hover test */
definedInFoo1: boolean;
definedInFoo2: string;
definedInFoo3: number;
/** Sets 2 to 'foo' */
fooMethod(): void;
});
It seems repeated structures aren't analyzed and flattened. I also don't see a way to flatten them myself. Changing CLASS to {[P in keyof CLASS]: CLASS[P]} will make each item reiterate every single property in HTMLElement in the d.ts.
This is a short reproducible, but actual code can get really heavy especially with mixins. I believe (but can't confirm), it causes the "Type instantiation is excessively deep and possibly infinite" error as well. There's probably a performance gain somewhere here as well. I know TS will flatten types, but not constructor return arguments. Maybe, simplified that's the core issue.
I have a second playground link that has a more verbose example including mixin patterns and extending classes extending classes:
Related:
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.