Skip to content

type tests #1337

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Mar 16, 2023
Merged

type tests #1337

merged 7 commits into from
Mar 16, 2023

Conversation

Fil
Copy link
Contributor

@Fil Fil commented Mar 14, 2023

@Fil Fil mentioned this pull request Mar 14, 2023
@Fil Fil marked this pull request as ready for review March 14, 2023 15:59
@Fil Fil requested review from duaneatat and mbostock March 14, 2023 15:59
src/options.js Outdated
@@ -128,7 +128,7 @@ export function arrayify(data) {
// An optimization of type.from(values, f): if the given values are already an
// instanceof the desired array type, the faster values.map method is used.
export function map(values, f, type = Array) {
return values instanceof type ? values.map(f) : type.from(values, f);
return !values ? values : values instanceof type ? values.map(f) : type.from(values, f);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My expectation was that the nullish (or falsey) check should happen earlier, not here in map. At this point we should know that values is non-null and an array or array-like.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know. Yes it was in valueof in #1024, but with the current code we need the test on lines 12, 14 and 16. And it felt repetitive to do:

  return valueType === "string"
    ? !data
      ? data
      : maybeTypedMap(data, field(value), type) // 12
    : valueType === "function"
    ? !data
      ? data
      : maybeTypedMap(data, value, type) // 14
    : valueType === "number" || value instanceof Date || valueType === "boolean"
    ? !data
      ? data
      : map(data, constant(value), type) // 16
    : value && typeof value.transform === "function"
    ? maybeTypedArrayify(value.transform(data), type)
    : maybeTypedArrayify(value, type);

I noticed that line 16 could be optimized as:
new (type ?? Array)(arrayify(data).length).fill(value)
or maybe even:
new (type ?? typeof value === "number" ? Float64Array : Array)(arrayify(data).length).fill(value)

(Both versions pass all tests.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we move the data == null test first to avoid repeating it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this?

export function valueof(data, value, type) {
  if (typeof value?.transform === "function") return maybeTypedArrayify(value.transform(data), type);
  if (data == null) return data;
  const valueType = typeof value;
  return valueType === "string"
    ? maybeTypedMap(data, field(value), type)
    : valueType === "function"
    ? maybeTypedMap(data, value, type)
    : valueType === "number" || value instanceof Date || valueType === "boolean"
    ? map(data, constant(value), type)
    : maybeTypedArrayify(value, type);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nope… if value is an array, we don't care if data is nullish. But from this variant we can test data==null in maybeTypedMap, which (contrary to map) is not used anywhere else.

Copy link
Member

@mbostock mbostock Mar 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if value is an array, we don't care if data is nullish

That’s not a requirement, and I don’t think that was intentional historically. The only time data should be nullish is for decorations—marks that are not data-driven, such as a frame. Decoration marks never have channels associated with them. And marks without data by definition don’t have an index, so even if they did theoretically have a channel, it wouldn’t be meaningful because there would be no index by which to lookup values in the channel. So, it may have been true that valueof returned the array in the valueof(null, array) case, but I don’t think we need to maintain that behavior. And we should add tests for it in any case.

Edit: Sigh. 😮‍💨 I forgot about raster and contour marks, which also utilize null data to compute “unindexed” channels when the value is expressed as a continuous function to be sampled. So I guess we have basically codified that valueof(null, array) and valueof(null, transform) should respect the value argument even though the data is nullish. So… disregard my comment. Everything is hard today it seems!

Copy link
Member

@mbostock mbostock Mar 15, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if we instead turned test/options.js into test/options.ts. Would we still need this file or could we rely on type validation of the other tests we are already running?

Edit: I guess this file is needed to assert when type errors should be generated? But that code that should not generate errors (the “valid input data” examples below) should just be part of test/options.ts like normal?

Also, should we name this src/options.test-d.ts or some such? I saw there are tools like tsd that specialize in this sort of thing. But I’d prefer to not add another dependency if tsc can do the checking for us.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we used tsd in the #1024 branch, but it's simpler to use tsc.

The "valid input data" were there to contrast with the ones below that expect ts errors, but they were not useful.

I've consolidated the tests (most of them were redundant).

@mbostock mbostock merged commit ba175c2 into main Mar 16, 2023
@mbostock mbostock deleted the fil/type-tests branch March 16, 2023 19:41
chaichontat pushed a commit to chaichontat/plot that referenced this pull request Jan 14, 2024
* tests salvaged from observablehq#1024

* fix crash in valueof when the data is not iterable

* avoid the nullish test in map

* move null test back to map

* consolidate tests

* remove "valid input data" type tests

* test array value

---------

Co-authored-by: Mike Bostock <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants