Description
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!