Skip to content

fix(ts-interface-generator): support classes inheriting from generic base classes#557

Open
CLVNSwan wants to merge 1 commit into
UI5:mainfrom
SWANGmbH:fix/generic-base-class-inheritance
Open

fix(ts-interface-generator): support classes inheriting from generic base classes#557
CLVNSwan wants to merge 1 commit into
UI5:mainfrom
SWANGmbH:fix/generic-base-class-inheritance

Conversation

@CLVNSwan
Copy link
Copy Markdown

Problem

When a class inherits from a generic base class (e.g., MyBaseClass<T>), the @ui5/ts-interface-generator skips the class entirely and does not generate a .gen.d.ts file.

Root Cause

TypeScript's type.isClassOrInterface() returns false for instantiated generic types. For example:

  • ControlisClassOrInterface() returns true
  • GenericControl<string>isClassOrInterface() returns false

The generator's getInterestingBaseClass() and getInterestingBaseSettingsClass() functions currently return early when isClassOrInterface() is false, causing all classes inheriting from generic base classes to be skipped.

Solution

For instantiated generic types, TypeScript stores the original generic class definition in the target property of the TypeReference. This fix checks for this property and uses it to continue the inheritance chain resolution.

Reproduction Example

import Control from "sap/ui/core/Control";
import type { MetadataOptions } from "sap/ui/core/Element";

// Generic base class extending a UI5 class
class GenericControl<T> extends Control {
  static readonly metadata: MetadataOptions = {
    properties: {
      value: { type: "object" }
    }
  };
}

/**
 * @namespace my.app.control
 */
export default class MyControl extends GenericControl<string> {
  static readonly metadata: MetadataOptions = {
    properties: {
      name: { type: "string", defaultValue: "" }
    }
  };
}

Before this fix: No MyControl.gen.d.ts is generated, no accessor methods like getName() / setName() are typed.

After this fix: MyControl.gen.d.ts is generated correctly with all typed accessor methods.

Changes

  • getInterestingBaseClass(): When isClassOrInterface() returns false, check if type.target exists and is a class/interface, then use that for inheritance resolution.
  • getInterestingBaseSettingsClass(): Same approach - when symbol is null, check type.target and recursively resolve.

Testing

Tested with a custom UI5 library containing generic base classes. All subclasses now correctly generate .gen.d.ts files.

…base classes

When a class inherits from a generic base class like `MyBaseClass<T>`,
TypeScript's `type.isClassOrInterface()` returns `false` for the
instantiated generic type. This caused the generator to skip such classes
entirely, not generating any `.gen.d.ts` files for them.

This fix checks for the `target` property on `TypeReference` types,
which contains the original generic class definition, and uses that
for the inheritance chain resolution.

Example that now works:
```typescript
// Generic base class extending ManagedObject
class GenericControl<T> extends Control {
  static metadata = { properties: { value: { type: 'object' } } };
}

// This class was previously skipped - now works correctly
class MyControl extends GenericControl<string> {
  static metadata = { properties: { name: { type: 'string' } } };
}
```
@cla-assistant
Copy link
Copy Markdown

cla-assistant Bot commented Mar 27, 2026

CLA assistant check
All committers have signed the CLA.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes @ui5/ts-interface-generator skipping classes that extend an instantiated generic base class (e.g. GenericBase<string>) by continuing inheritance resolution via ts.TypeReference.target.

Changes:

  • Extend getInterestingBaseClass() to unwrap instantiated generic TypeReferences via .target when type.isClassOrInterface() is false.
  • Extend getInterestingBaseSettingsClass() to retry resolution via .target when type.getSymbol() is unavailable for instantiated generic types.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/ts-interface-generator/src/interfaceGenerationHelper.ts
Comment thread packages/ts-interface-generator/src/interfaceGenerationHelper.ts
@akudev
Copy link
Copy Markdown
Member

akudev commented May 11, 2026

@CLVNSwan what is your use-case for a GenericControl<T>?

akudev
akudev previously approved these changes May 11, 2026
@akudev
Copy link
Copy Markdown
Member

akudev commented May 11, 2026

@CLVNSwan this seems to not pass CI: https://github.com/UI5/typescript/actions/runs/25680623026/job/75390791849
Have you run yarn ci locally?

@akudev akudev self-requested a review May 11, 2026 15:47
@akudev akudev dismissed their stale review May 11, 2026 15:48

CI failure

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants