Skip to content

Commit

Permalink
fix: failure on anonymous class with a method (backport #1824) (#1846)
Browse files Browse the repository at this point in the history
# Backport

This will backport the following commits from `main` to
`maintenance/v5.6`:
- [fix: failure on anonymous class with a method
(#1824)](#1824)

<!--- Backport version: 9.5.1 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

Co-authored-by: Rico Hermans <[email protected]>
  • Loading branch information
aws-cdk-automation and rix0rrr authored Mar 10, 2025
1 parent 1840c41 commit 3d4f5c9
Show file tree
Hide file tree
Showing 5 changed files with 155 additions and 17 deletions.
50 changes: 50 additions & 0 deletions fixtures/jsii-calc/lib/decorators.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
type Constructor = { new (...args: any[]): {} };

export function functionReturnsClasstype<T extends Constructor>(ctr: T) {
return class extends ctr {
};
}

/**
* A class decorator that changes inherited state and adds a readonly field to the class.
*
* This wasn't the thing that was exploding, see `function-returning-anonymous-class.ts` for that.
* Nevertheless, this makes for a good class decorator demo.
*/
export function classDecorator(x: typeof SomeDecoratedClass): typeof SomeDecoratedClass {
const ret = class extends x {
constructor() {
super();
this.state = this.state + this.state;
}
};

// This adds a field to the class, but we can't reflect that in the type because of the limitations
// of decorators. That's we advertise it through interface merging below.
(ret.prototype as any)['field'] = 'some_added_field';

return ret;
}

@classDecorator
export class SomeDecoratedClass {
protected state = 'state';

public accessState() {
return this.state;
}
}

export interface SomeDecoratedClass {
readonly field: string;
}

/**
* Exercise the above code
*/
function tryDecoratedClass() {
const instance = new SomeDecoratedClass();
return instance.field;
}
// Suppress unused locals warnings
void tryDecoratedClass;
17 changes: 17 additions & 0 deletions fixtures/jsii-calc/lib/function-returning-anonymous-class.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
type Constructor = { new (...args: any[]): {} };

/**
* Just the mere presence of this function is enough to break jsii, even if it's not exported from
* the jsii root module.
*
* The reason is that when we add deprecation warnings we visit all functions in all files.
*/
export function propertyInjectionDecorator<T extends Constructor>(ctr: T) {
// Important for the bug: the anonymous class extends something, *and*
// declares a method.
return class extends ctr {
public someMethod(): string {
return 'abc';
}
};
}
1 change: 1 addition & 0 deletions fixtures/jsii-calc/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export * from './stability';
export * from './submodules';
export * from './container-types';
export * from './indirect-implementation';
export * from './decorators';

export * as submodule from './submodule';
export * as onlystatic from './only-static';
Expand Down
6 changes: 5 additions & 1 deletion src/common/symbol-id.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,13 @@ interface SymbolIdOptions {
*/
export function symbolIdentifier(
typeChecker: ts.TypeChecker,
sym: ts.Symbol,
sym: ts.Symbol | undefined,
options: SymbolIdOptions = {},
): string | undefined {
if (!sym) {
return undefined;
}

// If this symbol happens to be an alias, resolve it first
// eslint-disable-next-line no-bitwise
while ((sym.flags & ts.SymbolFlags.Alias) !== 0) {
Expand Down
98 changes: 82 additions & 16 deletions test/__snapshots__/integration.test.ts.snap

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 3d4f5c9

Please sign in to comment.