Skip to content

build: Enable TypeScript type checking#7680

Open
camdecoster wants to merge 23 commits intomasterfrom
cam/7678/enable-typescript-type-checking
Open

build: Enable TypeScript type checking#7680
camdecoster wants to merge 23 commits intomasterfrom
cam/7678/enable-typescript-type-checking

Conversation

@camdecoster
Copy link
Copy Markdown
Contributor

@camdecoster camdecoster commented Jan 2, 2026

Description

Enable TypeScript type checking.

Closes #7678.

Changes

  • Add set of plotly.js types
  • Enable type checking
  • Update some files to TypeScript
  • Use ts-node for running some scripts
  • Add type generation script that uses attributes files
  • Add type checking steps to CI

Testing

  • CI should cover everything
  • Run npm run build locally to check that everything is bundled properly by esbuild
  • Run plotly.js devtools and load some mocks to make sure everything is working correctly
  • Run npm run typecheck to perform a type check on the source code. There should be no errors:
    $ npm run typecheck
    
    > plotly.js@3.5.1 typecheck
    > tsc --noEmit
    $

Notes

  • This PR adds the TypeScript compiler and typechecking via npm script
  • The provided types are a combination of plotly.js types from DefinitelyTyped and the schema (with Claude combining the two). This is a first pass and the types will need to be updated as files are converted.
  • (Most) IDEs will read the types and provide autocompletion once files are converted over
  • Read the README in src/types for an overview of how the types are set up
  • esbuild is compatible with TypeScript already, but it doesn't do type checking. To check types you need to run npm run typecheck. VS Code will also provide feedback as you're editing files.
  • I converted a few simple files to TypeScript as an example. I opted to use ESM syntax for these files which necessitated some changes in the require statements for the converted files. This is due to how esbuild determines if a default import should be namespaced or not.
  • Some of the converted files are used in scripts not processed by esbuild. Node 18 doesn't handle TS files natively, so these scripts need to be run with ts-node, a "TypeScript execution and REPL for node.js".
  • The TS config is pretty lenient right now to make it easier to convert things piece by piece. Once we make enough progress, this can be tightened up.
  • TS is not emitting files/types right now. esbuild handles converting the TS so the TS compiler won't need to do that. It would be valuable to have the compiler write the type definition files in the future. TS doesn't emit types, but there's a type generation step that uses TS to create types from attributes files.
  • There's an entry point for types now, but it's pointing to the internal types which can add overhead. In the future, there will be a type build step that compiles them down to a flat file.

@camdecoster camdecoster changed the title build: Enable typescript type checking build: Enable TypeScript type checking Jan 2, 2026
@camdecoster camdecoster marked this pull request as ready for review January 2, 2026 23:54
@camdecoster camdecoster requested a review from emilykl January 7, 2026 16:57
var constants = require('./constants');
var overrideAll = require('../../plot_api/edit_types').overrideAll;
var sortObjectKeys = require('../../lib/sort_object_keys');
var sortObjectKeys = require('../../lib/sort_object_keys').default;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@camdecoster Is it worth considering moving away from default exports entirely as part of this transition?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It only matters when one mixes CJS and ESM. That said, I don't think that we can avoid that for a long time. I suppose we could make just pick a direction to go and standardize. I could go either way, but the esbuild docs make it clear that they don't like default exports.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

There's an issue that covers converting from CJS to ESM in #7119. I left a comment mentioning your suggestion.

Comment thread src/types/core/data.d.ts Outdated
fillcolor?: string;
line?: Partial<Line>;
marker?: Partial<Marker>;
mode?: 'lines' | 'markers' | 'lines+markers' | 'none' | 'text' | 'lines+text' | 'markers+text' | 'lines+markers+text';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Does TypeScript have any support for 'flag list'-type string values such as this, where the string may consist of any number of a fixed set of values joined by a delimiter? I suppose not as it's fairly custom.

Otherwise could we use a custom type or a custom function to generate these lists of allowed values based on the flags?

Copy link
Copy Markdown
Contributor Author

@camdecoster camdecoster Jan 13, 2026

Choose a reason for hiding this comment

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

Yes, it's called a union type. What's shown on 133 is an example of that (though it's only used on that line). If we needed that type elsewhere, we could define it separately and reference it within the ScatterTrace type like this:

type Mode = 'lines' | 'markers' | 'lines+markers' | 'none' | 'text' | 'lines+text' | 'markers+text' | 'lines+markers+text';

export interface ScatterTrace extends TraceBase {
    ...,
    mode: Mode;
    ...,
}

Copy link
Copy Markdown
Contributor

@emilykl emilykl Jan 15, 2026

Choose a reason for hiding this comment

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

Yeah I guess I'm imagining something like a helper function which looks like flagList(flaglistVals, otherVals) such that

flaglistVals(['lines', 'markers', 'text'], ['none'])

returns

'lines' | 'markers' | 'lines+markers' | 'none' | 'text' | 'lines+text' | 'markers+text' | 'lines+markers+text';`

Comment thread src/types/core/data.d.ts Outdated
* Use specific trace types when available
*/
export interface GenericTrace extends TraceBase {
x?: any[];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm not sure there are any traces where x/y/z are allowed to be a type other than number[] | string[] but I could be wrong about that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

That's why it's permissive here. Once we become sure, we can change this as you suggest.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In this case maybe it would be easier to start stricter and loosen if needed?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actually, scratch that

@emilykl
Copy link
Copy Markdown
Contributor

emilykl commented Jan 15, 2026

@camdecoster Can you add a type-check step to the CI?

Comment thread src/types/core/layout.d.ts Outdated
yaxis?: Partial<LayoutAxis>;

// Multiple axes support (xaxis2, yaxis3, etc.)
[key: string]: any;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we tighten this layout spec here to match the plot schema?

@emilykl
Copy link
Copy Markdown
Contributor

emilykl commented Jan 15, 2026

@camdecoster Some initial thoughts on organization of the types:

For types corresponding directly to the schema:

  • Group them all under a single directory (e.g. types/core/schema/), maybe even in a single file if not too unwieldy
  • Use nested types where useful for repeated patterns (e.g. font options); these should use some kind of consistent naming scheme

Copy link
Copy Markdown

@codeCraft-Ritik codeCraft-Ritik left a comment

Choose a reason for hiding this comment

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

Great work! The implementation is clean and easy to understand

@camdecoster camdecoster assigned camdecoster and unassigned emilykl Apr 15, 2026
@camdecoster camdecoster force-pushed the cam/7678/enable-typescript-type-checking branch from 2439dad to de168ca Compare May 6, 2026 22:07
@camdecoster camdecoster requested a review from emilykl May 7, 2026 16:07
Comment thread src/types/core/data.d.ts Outdated
* Common properties shared by all trace types — convenience interface for
* gradual migration of internal code that doesn't need the full PlotData shape.
*/
export interface TraceBase {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What's the relationship between the TraceBase and PlotData types?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This is a leftover from a previous attempt at adding types. I'll remove it.

Comment thread src/types/core/data.d.ts
_length?: number;
_module?: any;
index?: number;
[key: string]: any;
Copy link
Copy Markdown
Contributor

@emilykl emilykl May 7, 2026

Choose a reason for hiding this comment

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

Is there any logic behind which keys are explicitly listed here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

These should be universally available internal properties. There could be more that need to be added, so the escape hatch is at the bottom. This will prevent TS errors from blocking the type check. We can add new properties as we come across them during conversion.

}

// ---------------------------------------------------------------------------
// LayoutAxis (extends Axis for cartesian subplots)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

small nit, but I'm not sure this comment is accurate since some of these properties seem to exist for non-cartesian axes as well

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're right! The naming could be better. Ideally, there would be separate types for each axis type. I'll add a task to fix that after this PR.

Comment thread src/types/core/layout.d.ts Outdated
// Mapbox types
// ---------------------------------------------------------------------------

export interface MapboxCenter {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

All of these Mapbox* types can be changed to Map* right? AFAIK the API is exactly the same.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good point. I'll rename them to Map* and add an alias to Mapbox* for backward compatibility.

Comment thread src/types/core/layout.d.ts Outdated
xaxis6: Partial<LayoutAxis>;
xaxis7: Partial<LayoutAxis>;
xaxis8: Partial<LayoutAxis>;
xaxis9: Partial<LayoutAxis>;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

hm I realize this is taken from DefinitelyTyped but what about plots with more than 9 x/y axes? I don't think there's any hard upper limit

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yeah, there's no limit. I'll update the types to allow any number.

hoversubplots: 'single' | 'overlaying' | 'axis';
calendar: Calendar;

// Dotted property paths for Plotly.relayout convenience
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why are these needed?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

relayout accepts an attribute string (eg. 'xaxis.range[0]'). Updating the type to handle anything string like that would remove much of the type safety. DT includes these commonly used attribute strings to allow for that type of signature while preserving type safety.

_modeBar?: any;
_pushmargin?: { [key: string]: any };

[key: string]: any;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

same question here, is there any reasoning behind which keys are called out explicitly?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Those were the properties found in the first search pass. More can be added as we convert files. I'll see if I can get a few more added.

/**
* Template figure type
*/
export interface TemplateFigure {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

What's the difference between Template and TemplateFigure?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

  • Template describes a config object that lists settings for different traces. You can define default styling, etc. per trace type.
  • TemplateFigure describes an actual figure that can be rendered. Think of what you might include in a devtools mock.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Shouldn't this be generated from src/components/colorbar/attributes.js?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It will be eventually. For now, most of the types have been manually added.

Comment thread src/types/traces/box.d.ts
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Doesn't this file duplicate the info in src/traces/box/attributes.js to some extent?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It does for now because that file hasn't been converted to TS yet. When it is, some of all of this info will be generated from the attributes file.

Comment thread src/types/ARCHITECTURE.md Outdated

| User-facing | Internal | Where defined |
|---|---|---|
| `Layout` | `FullLayout` | `Layout` is generated; `FullLayout extends Layout` is hand-written |
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

 `Layout` is generated; `FullLayout extends Layout` is hand-written

This isn't currently the case based on the contents of this PR though, right? Layout seems to be hand-written.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You're right. That's an aspirational statement. I'll update it to reflect the current status.


```ts
/**
* @generates ModeBar
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Should probably add some guidelines for choosing this canonical public name, since it may not always be obvious.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I added more info in the "Three things to notice" section below that covers this.


### 6. Replace the hand-written type in `src/types/`

Find the corresponding hand-written type (likely in
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Will it always be obvious which hand-written type corresponds to the attributes file? How do you determine for sure whether one already exists or not?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It should be obvious. If it's not, you could search in the types folder for some specific attributes and find it that way.

Comment thread src/types/CONVERTING_ATTRIBUTES.md Outdated
If the hand-written type was richer than the schema (e.g. used a narrowed
union where the schema says `string`), document the gap in a comment or
file an issue. Do not silently lose ergonomics — either improve the schema
(add `values: [...]`) or layer a hand-written refinement on top.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

or layer a hand-written refinement on top

I'm not sure there's really a clean way to do this using the proposed architecture

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

You could extend the generated type with the additional information without too much trouble. The clunky part would be the naming. For ModeBar, you could generate ModeBarGenerated then extend it as ModeBar. Not ideal, but it would work.

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.

[FEATURE]: Add TypeScript type checking

3 participants