FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

refactor(typescript): favor interface over type for object types by redfish4ktc · Pull Request #1146 · maxGraph/maxGraph · GitHub

forked from jgraph/mxgraph

refactor(typescript): favor interface over type for object types - #1146

Merged
redfish4ktc merged 2 commits into
mainfrom
refactor/ts_switch_from_type_to_interface
Aug 18, 2026
Merged

refactor(typescript): favor interface over type for object types#1146
redfish4ktc merged 2 commits into
mainfrom
refactor/ts_switch_from_type_to_interface

Conversation

redfish4ktc commented Aug 18, 2026
edited by coderabbitai Bot
Loading

Copy link
Copy Markdown
Member

Problem

Object types exposed by the package were declared with type aliases. Beyond the inconsistency with the declarations already using interface, a type alias cannot be extended with module augmentation: a consumer trying to declare an extra style property gets a TS2300 Duplicate identifier error. The only workarounds were casting or widening the style objects at every call site.

Changes

Enable @typescript-eslint/consistent-type-definitions with the interface option in eslint.config.mjs. The rule sits in the shared rules block, so it applies to every package, not only core.

Convert the declarations it reports, 27 across 14 files, mostly in packages/core/src/types.ts. The conversion is mechanical and was produced with eslint --fix.

CellStyle is converted by hand: it was an intersection (CellStateStyle & { ... }), a form the rule does not flag, and it is now interface CellStyle extends CellStateStyle. Two other intersections, GraphOptions and IdentityObject, are left as type aliases.

New extension point

Applications can now declare their own style properties:

declare module '@maxgraph/core' {
  interface CellStateStyle {
    myCustomStyleProperty?: number;
  }
}

Since CellStyle extends CellStateStyle, the declaration above applies to both. CellStyle can also be augmented on its own when the property only makes sense on the style declared on a cell and not on the computed state style. The inheritance stays one-way, so such a property does not leak onto CellStateStyle.

Both directions were checked by compiling an augmentation against the generated .d.ts files, including a @ts-expect-error on the leak case.

Impact for consumers

Not a breaking change for the vast majority of consumers, and JavaScript users are not impacted at all. This is documented in the CHANGELOG under Other Changes.

The only TypeScript behavior that differs is that an interface has no implicit index signature, so these types are no longer implicitly assignable to Record<string, unknown> or to a similar index-signature type.

The single occurrence in this repository is in StyleSheet.test.ts, where the CellStyle cast passed to expect.objectContaining becomes a satisfies clause. This keeps the literal checked against CellStyle while leaving it with the index signature the matcher requires.

Validation

The full CI chain was run locally: build -w packages/core, test-check -w packages/core, 561 tests in packages/core, test -w packages/ts-support, build-all-examples.bash, build -w packages/html, check:circular-dependencies, check:npm-package and lint. Example bundle sizes are unchanged.

Note on scope

packages/website contains one .tsx file with a violation, which is fixed here. It is not covered by the CI though: the lint script glob is **/*.ts, which does not match .tsx. Extending it would surface 3 unrelated pre-existing @typescript-eslint/no-require-imports errors in that same file, so it is left for a separate change.

Summary by CodeRabbit

  • Documentation

    • Updated the unreleased changelog with details about TypeScript interface usage and compatibility considerations.
    • Documented support for extending custom style properties through module augmentation.
  • Developer Experience

    • Improved TypeScript API declarations by representing exported object-shaped types as interfaces.
    • Style-related types now support clearer extension patterns while preserving existing properties and behavior.
    • Added consistency checks to encourage interface-based type definitions across the codebase.

Enable @typescript-eslint/consistent-type-definitions with the "interface" option and convert the 27 declarations it
reports across 14 files, mostly in packages/core/src/types.ts. CellStyle is converted by hand, since it was an
intersection and the rule therefore did not flag it.

Beyond consistency, interfaces support module augmentation while type aliases do not. Consumers can now declare their
own style properties on CellStateStyle and on CellStyle instead of casting or widening the style objects. This was
impossible before, the augmentation was rejected with "Duplicate identifier". CellStyle extends CellStateStyle rather
than intersecting it, so it is augmentable on its own while the inheritance stays one-way: a property added to
CellStyle does not leak onto CellStateStyle.

The only visible difference for TypeScript consumers is that an interface has no implicit index signature, so these
types are no longer implicitly assignable to Record<string, unknown>. The single occurrence in this repository is in
StyleSheet.test.ts, where the CellStyle cast passed to expect.objectContaining becomes a satisfies clause. This keeps
the literal checked against CellStyle while leaving it with the index signature the matcher requires.

The change is documented in the CHANGELOG under "Other Changes", as it is not a breaking change for the vast majority
of consumers and does not affect JavaScript users at all.
redfish4ktc added the refactor Code refactoring label Aug 18, 2026

coderabbitai Bot commented Aug 18, 2026
edited
Loading

Copy link
Copy Markdown

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info ⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 340269c2-fe52-4347-aac8-9b63e5dc3567

📥 Commits

Reviewing files that changed from the base of the PR and between 796c848 and e332d74.

📒 Files selected for processing (1)
  • packages/core/__tests__/view/style/StyleSheet.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/tests/view/style/StyleSheet.test.ts

Included review availability: Your plan includes up to 4 reviews per rolling hour; 3 remain after this review.


Walkthrough

The change converts object-shaped TypeScript type aliases to interfaces across core declarations and supporting code. ESLint now enforces interface declarations. Tests and the changelog document the updated syntax and TypeScript behavior.

Changes

Interface Migration

Layer / File(s) Summary
Core type contract conversion
eslint.config.mjs, packages/core/src/types.ts, packages/core/src/internal/types.ts, packages/core/src/i18n/config.ts, packages/core/src/serialization/ModelXmlSerializer.ts
Core object-shaped declarations now use interfaces with unchanged members. CellStyle extends CellStateStyle. ESLint requires interfaces for TypeScript type definitions.
Supporting package interface conversions
packages/core/src/view/event/EventSource.ts, packages/core/src/view/handler/config.ts, packages/core/src/view/image/ImageBundle.ts, packages/core/src/view/plugin/FitPlugin.ts, packages/core/src/view/style/config.ts, packages/html/stories/UserObject.stories.ts, packages/website/src/components/HomepageFeatures/index.tsx
Additional object-shaped declarations now use equivalent interfaces.
Validation and documentation updates
packages/core/__tests__/*, CHANGELOG.md
Test-local declarations use interfaces. Style assertions use satisfies CellStyle. The changelog documents module augmentation and implicit index-signature behavior.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: 🟡 Moderate · up to e332d

The updated declarations may prevent React 19 consumers from compiling because they reference the removed global JSX namespace. Merge should wait for that compatibility issue to be fixed or explicitly accepted by the owner.

Possibly related PRs

  • maxGraph/maxGraph#878: Updates the Stylesheet story to use the CellStyle interface and related TypeScript typings.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the refactor from object type aliases to interface declarations.
Description check ✅ Passed The description clearly covers the problem, implementation, extension point, consumer impact, validation, and scope of the change.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Choose a reason Spam Abuse Off Topic Outdated Duplicate Resolved Low Quality

Actionable comments posted: 1


ℹ️ Review info ⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 32466c7a-1f80-4128-b0de-478c106259dc

📥 Commits

Reviewing files that changed from the base of the PR and between 255ad00 and 796c848.

📒 Files selected for processing (17)
  • CHANGELOG.md
  • eslint.config.mjs
  • packages/core/__tests__/serialization/utils.ts
  • packages/core/__tests__/view/no-global-state-for-mixin-properties.ts
  • packages/core/__tests__/view/style/StyleSheet.test.ts
  • packages/core/__tests__/view/undoable-change/SelectionChange.test.ts
  • packages/core/src/i18n/config.ts
  • packages/core/src/internal/types.ts
  • packages/core/src/serialization/ModelXmlSerializer.ts
  • packages/core/src/types.ts
  • packages/core/src/view/event/EventSource.ts
  • packages/core/src/view/handler/config.ts
  • packages/core/src/view/image/ImageBundle.ts
  • packages/core/src/view/plugin/FitPlugin.ts
  • packages/core/src/view/style/config.ts
  • packages/html/stories/UserObject.stories.ts
  • packages/website/src/components/HomepageFeatures/index.tsx

Included review availability: Your plan includes up to 4 reviews per rolling hour; 2 remain after this review.

Apply the rule of this branch to the two extension-point aliases of the stylesheet tests, which the linter did not
report because they were intersections.

Document why these tests do not use module augmentation, even though it is now possible and is what an application
would do: an augmentation applies to the whole TypeScript program, so the custom properties would leak into every
other test of the package and silently weaken them.

Copy link
Copy Markdown

redfish4ktc merged commit 6323419 into main Aug 18, 2026
14 checks passed
redfish4ktc deleted the refactor/ts_switch_from_type_to_interface branch August 18, 2026 09:13
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

refactor Code refactoring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant


Back | FazBrowse Home | New Git URL