Skip to content

More precises return types for Plot.plot depending on the given PlotOptions parameter content #1418

Closed
@eschalkargans

Description

@eschalkargans

First of all, thank you so much for having added types! I'm so happy to see that this lib is now TypeScript-friendly.

I'm creating this issue to share an issue that I encountered in my codebase, and is likely to be encountered by others, that could lead to an improvement of the type definitions.

My situation

I have functions that wrap the Plot's API, like such:

export const createBarYChart = (
  data: ChartDateIndexedNumberSample[],
  width: number,
  height: number,
  ordinate: string
): HTMLElement => {

There was no issue before the types were added, because the typing was not enforced by the Observable Plot lib. This means the typing was only informative, for me, and was not an actual contract.

Since the types addition, I know see that my function definitions were actually wrong!.

Here is the error I get, as shown in VSCode:

const plot: (HTMLElement | SVGSVGElement) & Plot.Plot
Type '(HTMLElement | SVGSVGElement) & Plot' is not assignable to type 'HTMLElement'.
  Type 'SVGSVGElement & Plot' is missing the following properties from type 'HTMLElement': accessKey, accessKeyLabel, autocapitalize, dir, and 20 more.ts(2322)

The cause

When navigating to the Plot.plot type definition, here is what I see:

/**
 * Renders a new plot given the specified *options* and returns the
 * corresponding SVG element, or an HTML figure element if a caption or legend
 * is requested.
 */
export function plot(
    options?: PlotOptions
): (SVGSVGElement | HTMLElement) & Plot;

So, the (SVGSVGElement | HTMLElement) part of the return type is conditional, and can be summarized like this (this is pseudocode, not corresponding to the actual type definitions):

ReturnType = HTMLElement if (caption in PlotOptions) or (legend in PlotOptions) else SVGSVGElement

I already know in my call to Plot.plot that I do not use a caption nor a legend, so it would be great to get a SVGSVGElement return type rather than the union, to avoid type assertions.

How TypeScript could help

If I know in advance, by only reading my code that I expect a SVGSVGElement, there must be a way to communicate it to the TypeScript compiler.

Maybe Conditional Types could help, or function overloads?

Regarding caption:
We can check the presence of the caption property immediately in the PlotOptions.

Regarding legend:
This one is more tricky as it can be present at multiple places, and on nested interfaces in PlotOptions (eg in ScaleOptions).

Note: I am really not sure how this "conditional return type" could be implemented, or if it is feasible in the first place. Anyway, I think that the interface can be more explicit about what kind of object it returns!

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions