3

I am investigating the creation of a Typescript Definition file for Plotly.js. I have looked at a number of example definition files on the DefinitelyTyped repository. One question that comes to mind is the interface for the Data object. The Data object is an array of Traces, as demonstrated in the plot-schema.

Condsider this example of the data object.

data = [
    {
         type: 'scatter',  
         x: [1, 2, 3],     
         y: [3, 1, 6],     
         marker: {         
             color: 'rgb(16, 32, 77)' 
         }
    },
    {
         type: 'bar',      
         x: [1, 2, 3],     
         y: [3, 1, 6],     
         name: 'bar chart example' 
    }
];

The trace types, such as 'scatter' and 'bar', can have different attributes. For example, the 'pie' trace type has a "direction" attribute.

While I would like to define separate interfaces for each trace type, with common attributes factored into a higher-level interface, I do not see a good way to do so.

The most intuitive approach I can think of is something like this...

interface Trace {
    x: Array<any>;
    y: Array<any>;
}

interface BarTrace extends Trace {
   type: 'bar';
   // bar specific members
}

interface ScatterTrace extends Trace {
    type: 'scatter';
    // scatter specific members
}

This is not valid typescript. A type is expected, rather than the string 'bar' or 'scatter'.

Is anyone aware of other libraries with a similar structure that have typescript definitions, or a better way to structure the typescript definition file for this JSON structure?

Bill Kidwell
  • 280
  • 2
  • 9
  • Would it work to put `type : 'path' | 'point' | 'polygon';` in the parent `Trace` type? This would not fix the child types to the specific strings, but giving an interface a fixed instance property that does not change doesn't really make sense in a typing system anyway-- the type has a name (`BarTrace`, `ScatterTrace`) that identifies it & that type property does the same thing. – Stop Slandering Monica Cellio Jun 13 '16 at 20:41
  • I'll give this a try. I haven't looked at it in a while. Thanks for the suggestion. – Bill Kidwell Jun 16 '16 at 16:34

1 Answers1

1

I think your best option is to proceed as you are, but define type as a string. Something like the following:

interface Trace {
    type: string;
    x: Array; 
    y: Array; 
}

interface BarTrace extends Trace {
   // bar specific members
}

interface ScatterTrace extends Trace {
    // scatter specific members
}

let data: Array<Trace> = [
    {
         type: 'scatter',  
         x: [1, 2, 3],     
         y: [3, 1, 6],     
         marker: {         
             color: 'rgb(16, 32, 77)' 
         }
    },
    {
         type: 'bar',      
         x: [1, 2, 3],     
         y: [3, 1, 6],     
         name: 'bar chart example' 
    }
];

Once you determine if the type is "scatter" or "bar", you can cast the variable as follows.

data.forEach((item) => {
    if (item.type === 'scatter') {
        let trace: ScatterTrace = item;
        // ...
    } else {
        let trace: BarTrace = item;
        // ...
    }
});
wjohnsto
  • 4,323
  • 2
  • 14
  • 20