I’m struggling to utilize d3’s .extent() function in typescript.
I’ve got some code here that creates Date objects out of dates
const dates:(Date | null)[] = data.map(row => d3.timeParse("%Y-%m-%d")(row.Date))
When I console log the dates variable, it looks something like this
[Sat Jun 24 2023 00:00:00 GMT-0700 (Pacific Daylight Time), Sat Jun 24 2023 00:00:00 GMT-0700 (Pacific Daylight Time), Fri Jun 23 2023 00:00:00 GMT-0700 (Pacific Daylight Time), Fri Jun 23 2023 00:00:00 GMT-0700 (Pacific Daylight Time), Fri Jun 23 2023 00:00:00 GMT-0700 (Pacific Daylight Time), ...]
Therefore, I know my dates variable is full of Date objects, like expected…
However, when I try and use the d3.extent() function to find the highest and lowest dates
const xScale = d3
.scaleTime()
.domain(d3.extent(dates))
Argument of type '[undefined, undefined] | [string, string]' is not assignable to parameter of type 'Iterable<Date | NumberValue>'.
I’m confused as to why this might be, though, as my dates variable is in fact exclusively dates, and not, as is expressed in the error, ‘[undefined, undefined] | [string, string]’.
What is wrong about what I am passing in? How should I be passing in dates to the extent() function in typescript?
2
Answers
The issue you’re facing is related to the fact that the
map()
function can potentially returnnull
values in yourdates
array. This is because thed3.timeParse()
function might returnnull
if it fails to parse a date string.To fix this issue, you can filter out the
null
values from thedates
array before passing it to thed3.extent()
function. Here’s an example of how you can do it:By using the
filter()
function, you remove anynull
values from thedates
array, ensuring that only validDate
objects are passed to thed3.extent()
function.This should resolve the error you’re encountering and allow you to correctly calculate the extent of your dates.
Filtering for
null
instances is a good approach, but you get:Type '(Date | null)[]' is not assignable to type 'Date[]'. Type 'Date | null' is not assignable to type 'Date'. Type 'null' is not assignable to type 'Date'
.That is because TypeScript is still considering the possibility of
null
values in your array even after the filter operation.You should include a type predicate
date is Date
in the filter function. That explicitly tells TypeScript that the result of the filter will only includeDate
objects, and nonull
values.That does create a filter function that not only checks if the date is not
null
, but also uses a type guard to tell TypeScript that every item that passes this filter is aDate
.(See also "Type predicates: Filtering arrays in Typescript" from Shreejit Rajbanshi)
Followed with:
It uses
as [Date, Date]
to tell TypeScript to trust us that the domain will be a tuple of twoDate
objects.The
as [Date, Date]
is a type assertion, it is not changing the actual value or operation of the code, but rather just informing TypeScript of the type. That will not protect you fromnull
or undefined values at runtime, it is purely for TypeScript’s static type checking. Hence, why we did thenull
check before during the filter operation.