vega / react-vega

Convert Vega spec into React class conveniently
http://vega.github.io/react-vega/
Other
385 stars 67 forks source link

Fix example bugs + typing wonkiness #141

Closed aaron-fuleki closed 4 years ago

aaron-fuleki commented 4 years ago

A few things I noticed in trying to get the examples to work (per this README):

  1. It looks like createClassFromSpec now only expects 1 argument (just the spec), so I removed the 'BarChart' arg.
  2. The sample spec refers to the signal as tooltip, not hover (breaking signalListeners).
  3. I can't for the life of me figure out the spec typing.

The first two were easy fixes, but number three has me stumped. I've re-read the Vega typings a dozen times, and I don't understand it. The errors I see are like this:

(property) spec: VisualizationSpec
Type '{ width: number; height: number; data: { name: string; }[]; signals: { name: string; value: {}; on: { events: string; update: string; }[]; }[]; scales: ({ name: string; type: string; domain: { data: string; field: string; }; range: string; nice?: undefined; } | { ...; })[]; axes: { ...; }[]; marks: ({ ...; } | { ......' is not assignable to type 'VisualizationSpec'.
  Type '{ width: number; height: number; data: { name: string; }[]; signals: { name: string; value: {}; on: { events: string; update: string; }[]; }[]; scales: ({ name: string; type: string; domain: { data: string; field: string; }; range: string; nice?: undefined; } | { ...; })[]; axes: { ...; }[]; marks: ({ ...; } | { ......' is not assignable to type 'Spec'.
    Types of property 'scales' are incompatible.
      Type '({ name: string; type: string; domain: { data: string; field: string; }; range: string; nice?: undefined; } | { name: string; domain: { data: string; field: string; }; nice: boolean; range: string; type?: undefined; })[]' is not assignable to type 'Scale[]'.
        Type '{ name: string; type: string; domain: { data: string; field: string; }; range: string; nice?: undefined; } | { name: string; domain: { data: string; field: string; }; nice: boolean; range: string; type?: undefined; }' is not assignable to type 'Scale'.
          Type '{ name: string; type: string; domain: { data: string; field: string; }; range: string; nice?: undefined; }' is not assignable to type 'Scale'.
            Type '{ name: string; type: string; domain: { data: string; field: string; }; range: string; nice?: undefined; }' is not assignable to type 'ThresholdScale'.
              Types of property 'type' are incompatible.
                Type 'string' is not assignable to type '"threshold" | undefined'.ts(2322)
createClassFromSpec.d.ts(11, 5): The expected type comes from property 'spec' which is declared here on type '{ mode?: "vega" | "vega-lite" | undefined; spec: VisualizationSpec; }'

I've tried building a spec line-by-line, following the typings verbatim, but I keep getting what seem to be contradictory type errors. I can never get things like scale to stop throwing type errors, no matter what combination I try. I'm staring right at the type definitions, thinking I follow how they extend each other, but still no dice.

Here's a sandbox that shows the error: https://codesandbox.io/s/react-vega-typescript-48u9k

Just hover over the spec arg on BarChart.tsx:76 to see it.

The chart still renders in this sandbox, but my prod environment is much more strict (using Next.js 9.3), so it kills the build.

Should I open a ticket in vega-typings?

EDIT: I didn't realize vega-typings was archived, it was just the first Google hit I got. I see in in my IDE that the typings in fact come from the vega package itself now.

stuartlynn commented 4 years ago

I am having similar issues trying to use a vega-lite specification. I am struggling to understand where to import Mark types like BAR etc and range types like ORDINAL. Currently I am just redefining these in my project but would much rather use the official ones in the vega packages.

For example with the following spec

const spec = {
    width: 400,
    height: 200,
    mark: BAR,
    encoding: {
        x: { field: 'a', type: ORDINAL },
        y: { field: 'b', type: QUANTITATIVE },
    },
    data: { name: 'table' }, // note: vega-lite data attribute is a plain object instead of an array
}

I need to manually define these types in my own project to get them to work:

export type Mark =
    | typeof ARC
    | typeof AREA
    | typeof BAR
    | typeof LINE
    | typeof IMAGE
    | typeof TRAIL
    | typeof POINT
    | typeof TEXT
    | typeof TICK
    | typeof RECT
    | typeof RULE
    | typeof CIRCLE
    | typeof SQUARE
    | typeof GEOSHAPE;

export const QUANTITATIVE: 'quantitative' = 'quantitative';
export const ORDINAL: 'ordinal' = 'ordinal';
export const TEMPORAL: 'temporal' = 'temporal';
export const NOMINAL: 'nominal' = 'nominal';

@aaron-fuleki do you have any typescript code examples of where you got the types to import and work?

aaron-fuleki commented 4 years ago

@stuartlynn nope, sorry. I just renamed my component files to *.jsx and moved on. My TypeScript-fu was clearly not up to the task 😛

I would very much to figure this out, whenever I circle back to building more visualization components. Type-safety would go a long way toward preventing borkage, especially when other teams will be using the components or working on the data API they consume.

robert-moore commented 4 years ago

Can confirm there are type errors thrown on the demos: Using this schema as spec

export default {
  $schema: 'https://vega.github.io/schema/vega/v5.json',
  width: 400,
  height: 200,
  padding: { left: 5, right: 5, top: 5, bottom: 5 },

  data: [
    {
      name: 'table',
      values: [
        { category: 'A', amount: 28 },
        { category: 'B', amount: 55 },
        { category: 'C', amount: 43 },
        { category: 'D', amount: 91 },
        { category: 'E', amount: 81 },
        { category: 'F', amount: 53 },
        { category: 'G', amount: 19 },
        { category: 'H', amount: 87 }
      ]
    }
  ],

  signals: [
    {
      name: 'tooltip',
      value: {},
      on: [
        { events: 'rect:mouseover', update: 'datum' },
        { events: 'rect:mouseout', update: '{}' }
      ]
    }
  ],

  scales: [
    {
      name: 'xscale',
      type: 'band',
      domain: { data: 'table', field: 'category' },
      range: 'width'
    },
    {
      name: 'yscale',
      domain: { data: 'table', field: 'amount' },
      nice: true,
      range: 'height'
    }
  ],

  axes: [
    { orient: 'bottom', scale: 'xscale' },
    { orient: 'left', scale: 'yscale' }
  ],

  marks: [
    {
      type: 'rect',
      from: { data: 'table' },
      encode: {
        enter: {
          x: { scale: 'xscale', field: 'category', offset: 1 },
          width: { scale: 'xscale', band: 1, offset: -1 },
          y: { scale: 'yscale', field: 'amount' },
          y2: { scale: 'yscale', value: 0 }
        },
        update: {
          fill: { value: 'steelblue' }
        },
        hover: {
          fill: { value: 'red' }
        }
      }
    },
    {
      type: 'text',
      encode: {
        enter: {
          align: { value: 'center' },
          baseline: { value: 'bottom' },
          fill: { value: '#333' }
        },
        update: {
          x: { scale: 'xscale', signal: 'tooltip.category', band: 0.5 },
          y: { scale: 'yscale', signal: 'tooltip.amount', offset: -2 },
          text: { signal: 'tooltip.amount' },
          fillOpacity: [{ test: 'datum === tooltip', value: 0 }, { value: 1 }]
        }
      }
    }
  ]
};

I get this error

No overload matches this call.
  Overload 1 of 2, '(props: Readonly<VegaProps>): Vega', gave the following error.
    Type '{ $schema: string; width: number; height: number; padding: { left: number; right: number; top: number; bottom: number; }; data: { name: string; values: { category: string; amount: number; }[]; }[]; signals: { name: string; value: {}; on: { ...; }[]; }[]; scales: ({ ...; } | { ...; })[]; axes: { ...; }[]; marks: ({ ....' is not assignable to type 'VisualizationSpec'.
      Type '{ $schema: string; width: number; height: number; padding: { left: number; right: number; top: number; bottom: number; }; data: { name: string; values: { category: string; amount: number; }[]; }[]; signals: { name: string; value: {}; on: { ...; }[]; }[]; scales: ({ ...; } | { ...; })[]; axes: { ...; }[]; marks: ({ ....' is not assignable to type 'Spec'.
        Types of property 'scales' are incompatible.
          Type '({ name: string; type: string; domain: { data: string; field: string; }; range: string; nice?: undefined; } | { name: string; domain: { data: string; field: string; }; nice: boolean; range: string; type?: undefined; })[]' is not assignable to type 'Scale[]'.
            Type '{ name: string; type: string; domain: { data: string; field: string; }; range: string; nice?: undefined; } | { name: string; domain: { data: string; field: string; }; nice: boolean; range: string; type?: undefined; }' is not assignable to type 'Scale'.
              Type '{ name: string; type: string; domain: { data: string; field: string; }; range: string; nice?: undefined; }' is not assignable to type 'Scale'.
                Type '{ name: string; type: string; domain: { data: string; field: string; }; range: string; nice?: undefined; }' is not assignable to type 'ThresholdScale'.
                  Types of property 'type' are incompatible.
                    Type 'string' is not assignable to type '"threshold" | undefined'.
  Overload 2 of 2, '(props: VegaProps, context?: any): Vega', gave the following error.
    Type '{ $schema: string; width: number; height: number; padding: { left: number; right: number; top: number; bottom: number; }; data: { name: string; values: { category: string; amount: number; }[]; }[]; signals: { name: string; value: {}; on: { ...; }[]; }[]; scales: ({ ...; } | { ...; })[]; axes: { ...; }[]; marks: ({ ....' is not assignable to type 'VisualizationSpec'.
      Type '{ $schema: string; width: number; height: number; padding: { left: number; right: number; top: number; bottom: number; }; data: { name: string; values: { category: string; amount: number; }[]; }[]; signals: { name: string; value: {}; on: { ...; }[]; }[]; scales: ({ ...; } | { ...; })[]; axes: { ...; }[]; marks: ({ ....' is not assignable to type 'Spec'.ts(2769)
VegaEmbed.d.ts(6, 5): The expected type comes from property 'spec' which is declared here on type 'IntrinsicAttributes & IntrinsicClassAttributes<Vega> & Pick<Readonly<VegaProps> & Readonly<{ children?: ReactNode; }>, "style" | ... 27 more ... | "timeFormatLocale"> & Partial<...> & Partial<...>'
kristw commented 4 years ago

Just published 7.4.0 with type VisualizationSpec re-exported as part of the module (#242 ). The example in demo are also updated to correctly typed.