Version: @tanstack/charts 0.4.0, d3-scale 4.0.2, TypeScript 5.x/6.x
Repro
The scales reference documents passing a configured instance for fixed domains ("Pass a scale instance when the domain is fixed application state: scale: scaleLinear().domain([0, 100])"). With some mark unions this fails to typecheck:
const definition = defineChart({
marks, // e.g. lineY marks whose x is Date | string, plus custom marks
x: { scale: scaleTime().domain([start, end]) }, // TS2769
y: { scale: scaleLinear },
});
Type 'ScaleTime<number, number, never>' is not assignable to type
'ChartScale | ChartScaleInput<NonNullable<string | Date>>'.
Type 'ScaleTime<number, number, never>' is not assignable to type
'ConfiguredScaleLike<NonNullable<string | Date>>'.
The types returned by 'range(...)' are incompatible between these types.
Type 'number[]' is missing the following properties from type
'ConfiguredScaleLike<NonNullable<string | Date>>': copy, domain, range
Two structural mismatches:
- d3's
range() is overloaded (range(): Range[] / range(range): this); when TS checks assignability against the single signature range: (values: Iterable<number>) => ConfiguredScaleLike<TValue>, it can select the zero-arg overload and fail on the return type.
- When the marks union widens the axis
TValue to include string, ScaleTime's callable (value: Date | number) => number no longer covers the value union even though runtime usage is fine.
Runtime works in all these cases (the resolver detects instances via .copy and copies them), so this is types-only; consumers end up writing as unknown as ConfiguredScaleLike<...>.
Expected
ChartScaleInput accepts real d3 scale instances without casts — e.g. loosening range to an overload-tolerant shape and/or making instance detection a separate structural type ({ copy(): unknown }-based) that skips the callable/range checks the resolver doesn't rely on.
Happy to open a PR for the types if the direction sounds right.
Version: @tanstack/charts 0.4.0, d3-scale 4.0.2, TypeScript 5.x/6.x
Repro
The scales reference documents passing a configured instance for fixed domains ("Pass a scale instance when the domain is fixed application state:
scale: scaleLinear().domain([0, 100])"). With some mark unions this fails to typecheck:Two structural mismatches:
range()is overloaded (range(): Range[]/range(range): this); when TS checks assignability against the single signaturerange: (values: Iterable<number>) => ConfiguredScaleLike<TValue>, it can select the zero-arg overload and fail on the return type.TValueto includestring,ScaleTime's callable(value: Date | number) => numberno longer covers the value union even though runtime usage is fine.Runtime works in all these cases (the resolver detects instances via
.copyand copies them), so this is types-only; consumers end up writingas unknown as ConfiguredScaleLike<...>.Expected
ChartScaleInputaccepts real d3 scale instances without casts — e.g. looseningrangeto an overload-tolerant shape and/or making instance detection a separate structural type ({ copy(): unknown }-based) that skips the callable/rangechecks the resolver doesn't rely on.Happy to open a PR for the types if the direction sounds right.