D3 axis and typescript
What is typescript and why it is awesome to use it. How are d3 functions typed.
A few examples on how it works.
Most basic linear axis
A graph axis is based on a scale. But what is a scale? This is how thed3-scale module describes it:
Scales are a convenient abstraction for a fundamental task in visualization: mapping a dimension of abstract data to a visual representation
So basically a scale is a function. You provide it with an input like the value of a data point, and it returns another value like a position in pixel.
Here is a very basic implementation of a scale using the
scaleLinear function of d3.
const xScale = d3.scaleLinear() .domain([0, 10]) // data can go from 0 to 10 .range([0, width]); // it will result in a value that goes from 0 to width // xScale(0) -> 0 // xScale(10) -> width // xScale(5) -> width / 2
Scales are then used to build axis. Here is an example transforming the scale above in an X axis using a custom react component.
How to build a bottom axis and a left axis component using React, used to render a d3 scale.
Ok, now, how to deal with this in a typescript world?
scaleLinear and typescript
When we create a scale function using
scaleLinear, it is equivalent as calling the function with the 3 default generics
// if you call const xScale = d3.scaleLinear() // it is equivalent to const xScale = d3.scaleLinear<numeric, numeric, never>()
Here, the first generic is the type of the data items used for the range of scale. The second generic is the type of the data that the scale with output. You can see the explanations more in depth in the type definition of the scaleLinear function.
Now, what happens if we want to use scaleLinear to take a numeric value and map it to a color? In this case the range is gonna be an array of string. And the output is gonna be a color too. We can type it as:
// typing a color scale const colorScale = d3.scaleLinear<string, string, never>() .range(['blue', 'green']) .domain([1, 100]);
scaleOrdinal and typescript
Consider a finite number of groups. We need to assign a specific color to each group. This is called an ordinal scale and is implemented in the d3
What's needed here is thus a list of colors to use (the
range) and a list of group names: the
To put it in a nutshell, that's how the color scale is implemented:
// List of arbitrary colors const COLORS = ["#e0ac2b", "#e85252", "#6689c6", "#9a6fb0", "#a53253"]; // List of all group names const allGroups = data.map((group) => group.group); // Color scale const colorScale = d3.scaleOrdinal<string>() .domain(allGroups) .range(COLORS);
You can see how scaleOrdinal type implementation