Skip to content

[RFC] TypedDataKey#6867

Closed
PavelVanecek wants to merge 4 commits into
mainfrom
getTypedValue
Closed

[RFC] TypedDataKey#6867
PavelVanecek wants to merge 4 commits into
mainfrom
getTypedValue

Conversation

@PavelVanecek

@PavelVanecek PavelVanecek commented Jan 10, 2026

Copy link
Copy Markdown
Collaborator

Description

Proof of concept of how we could add strict typing to our chart data. Includes new types for DataKey and get getValueByDataKey, and demo usage on Area component.

There should be no change in behavior, all is just types.

I'm looking for opinions. Once we have agreed this is the way to go I will work on removing the old type definition completely.

Benefits:

  • Backwards compatible - people who do nothing will continue seeing any
  • Now TypeScript will see the relationship between data and dataKey and check that the key matches the data shape
  • Thanks to this, IDEs can start showing autocomplete (at least mine does) in dataKey

Cons:

  • This type checking only works within one component. So it works when both data and dataKey are in an Area, but out of the box it won't work when data is on AreaChart and dataKey is on Area.
  • To work around that I have added optional generic to Area where one can declare explicitly what is the expected type, and then TypeScript again has a chance to verify
  • The syntax for this is awkward, the pointy brackets are sometimes JSX and sometimes generics. On top of that I would also like to add function similar to what Redux is doing with an extra function or method: https://redux.js.org/usage/usage-with-typescript#withtypes I am still contemplating the exact shape for Recharts.

Related Issue

#6645

How Has This Been Tested?

All tests and types are passing - the build output and omnidoc are not, I will fix before merging.

Screenshots (if appropriate):

Types of changes

  • Bug fix (non-breaking change which fixes an issue)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Types only

Checklist:

  • My change requires a change to the documentation.
  • I have updated the documentation accordingly. TODO
  • I have added tests to cover my changes.
  • I have added a storybook story or VR test, or extended an existing story or VR test to show my changes

Summary by CodeRabbit

Release Notes

  • Refactor

    • Improved data extraction and value handling across chart components with a new typed data key system.
    • Added generic type parameters to Area, Bar, and related components for enhanced type inference.
    • Updated utility functions for more flexible and robust data handling with stricter null-safety checks.
    • Refined data domain truncation logic for edge cases.
  • Chores

    • Enhanced ESLint configuration for stricter type checking.
    • Expanded test coverage for typed data access and component generics.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai

coderabbitai Bot commented Jan 10, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

This pull request introduces a typed data access layer to replace generic getValueByDataKey usage throughout Recharts. A new getTypedValue utility with multiple overloads provides flexible, type-safe value extraction. Components are updated to use this new utility, component signatures gain generic type parameters, and utility functions for functional composition are removed from the scale module.

Changes

Cohort / File(s) Summary
New typed value extraction utility
src/util/getTypedValue.ts
Introduces new module with TypedDataKey<T, R> type and 7+ overloaded getTypedValue() function for flexible, type-safe data access supporting property keys, functional resolvers, and array indices. Includes default value handling and runtime type narrowing.
Type system refactoring
src/util/types.ts, src/state/chartDataSlice.ts, src/state/types/AreaSettings.ts, src/state/selectors/areaSelectors.ts
Updates DataKey<T> to alias TypedDataKey<T>, makes ChartData generic with default parameter, changes AreaSettings.dataKey from DataKey<any> to TypedDataKey, and loosens AreaPointItem.value to ReadonlyArray<unknown>.
Cartesian component typing
src/cartesian/Area.tsx, src/cartesian/Bar.tsx
Adds generic type parameters to Area component (DataPointType, ValueAxisType), updates AreaProps interface with generics, replaces getValueByDataKey with getTypedValue, introduces stricter typing for value arrays and domain truncation, adjusts data extraction and null-safety checks.
Polar component refactoring
src/polar/Pie.tsx, src/polar/RadialBar.tsx
Replaces getValueByDataKey with getTypedValue for data value extraction; RadialBar adds explicit null-handling for truncation results.
Utility function updates
src/util/ChartUtils.ts
Adds optional defaultValue parameter to getValueByDataKey, marks function as deprecated via JSDoc, widening truncateByDomain parameter types and changing return type to `[number, number]
Label and text rendering
src/component/Text.tsx, src/component/LabelList.tsx
Adds exported type guard isRenderableText(val: unknown): val is RenderableText, updates LabelListEntry.value type to unknown, changes valueAccessor return type to RenderableText, and integrates getTypedValue for value extraction with runtime renderable-text validation.
Tooltip and chart rendering
src/chart/Sankey.tsx
Replaces getValueByDataKey with getTypedValue for node and link payload value extraction in tooltip construction.
Scale utilities cleanup
src/util/scale/util/utils.ts, src/util/scale/getNiceTickValues.ts
Deletes entire utility module removing curry, range, map, compose and placeholder mechanisms; updates getNiceTickValues to use explicit loops and Array.fill(Infinity) instead of functional composition.
ESLint configuration
eslint.config.mjs
Adds new "no-any/config" block targeting src/util/scale/** with strict rules for no-explicit-any, ban-ts-comment, and consistent-type-assertions.
Test additions and updates
test/cartesian/Area.typed.spec.tsx, test/util/getTypedValue.spec.tsx
Introduces comprehensive typing-focused test suites for Area component generics and getTypedValue overload patterns, covering implicit/explicit typing, dataKey functions, and type inference validation.
Existing test migrations
test/cartesian/Area.spec.tsx, test/state/selectors/*.spec.tsx, test/util/ChartUtils/truncateByDomain.spec.ts, test/util/scale/utils.spec.ts
Updates test data shapes from primitive arrays to object-based arrays with explicit keys, removes deleted utility tests, adjusts expectations for truncateByDomain to return undefined instead of original values, removes usage of Customized component.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

typescript, refactor

Suggested reviewers

  • ckifer
🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title '[RFC] TypedDataKey' is vague and uses abbreviation without context; it doesn't clearly convey what the main change is to someone unfamiliar with the codebase. Consider a more descriptive title like 'Add strict TypeScript typing for chart data keys' or '[RFC] Introduce typed data key extraction for type-safe data access' to better communicate the main objective.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The pull request description covers key sections including description, related issue, motivation/context, testing approach, types of changes, and checklist status; it thoroughly explains the PoC objectives, benefits, limitations, and remaining work.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch getTypedValue

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@PavelVanecek
PavelVanecek requested a review from ckifer January 10, 2026 12:05
@PavelVanecek

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jan 10, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/chart/Sankey.tsx (1)

491-503: Update line 502 to use getTypedValue for consistency and type safety.

The tooltip payload construction in the 'source'/'target' branch uses getValueByDataKey(payload, 'value') while the 'node' branch and other similar extractions (lines 491-492, 496-497) use getTypedValue. Change line 502 to getTypedValue(payload, 'value') to complete the migration to the more type-safe function and maintain consistency.

🤖 Fix all issues with AI agents
In @src/polar/RadialBar.tsx:
- Around line 635-643: computeRadialBarDataItems currently returns nulls from
its map callback but is typed as ReadonlyArray<RadialBarDataItem>; change its
return type to ReadonlyArray<RadialBarDataItem | null> (update the function
signature for computeRadialBarDataItems) so the type matches possible nulls, and
update selectRadialBarSectors to filter out nulls before returning (e.g., append
.filter(Boolean) to the array returned by selectRadialBarSectors) so consumers
like RadialBarSectors and RadialBarLabelListProvider receive only non-null
RadialBarDataItem entries.

In @src/util/scale/getNiceTickValues.ts:
- Around line 179-186: The branch handling infinite cormin/cormax uses
Array(tickCount - 1) which can throw if tickCount <= 0; replace uses of
tickCount with the already-normalized count (from const count =
Math.max(tickCount, 2)) so the filled arrays are built with Array(count - 1) and
the returned values respect the normalized tick count; update the two spread
constructions that currently use Array(tickCount - 1) to use Array(count - 1)
(keeping the existing cormin/cormax placement and the min > max reverse logic).
🧹 Nitpick comments (3)
src/util/scale/getNiceTickValues.ts (1)

88-96: Loop-based tick generation looks behavior-preserving; minor type nit

This should match the previous “generate indices then map” behavior. Minor: prefer const ticks: number[] = []; (same meaning, more idiomatic).

test/cartesian/Area.typed.spec.tsx (1)

33-39: Consider adding vi.useFakeTimers() for timer-dependent tests.

Per coding guidelines, tests should use vi.useFakeTimers() due to Redux autoBatchEnhancer dependency on timers. While these tests may work without it, adding fake timers ensures consistent behavior.

🔧 Suggested addition
 describe('Area with strong typing', () => {
+  beforeEach(() => {
+    vi.useFakeTimers();
+  });
+
+  afterEach(() => {
+    vi.useRealTimers();
+  });
+
   describe('with all implicit types', () => {
src/cartesian/Area.tsx (1)

121-172: Generic AreaProps enables type-safe dataKey usage.

The generic parameters DataPointType and ValueAxisType allow consumers to specify their data shape and get compile-time validation of dataKey. The dataKey prop on line 171 uses TypedDataKey<DataPointType, ValueAxisType> to enforce the relationship.

The commented-out line 172 (// dataKey: DataKey<T>;) appears to be leftover from development.

🧹 Remove commented code
   dataKey: TypedDataKey<DataPointType, ValueAxisType>;
-  // dataKey: DataKey<T>;
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8ae7d8b and f1f8ec2.

📒 Files selected for processing (24)
  • eslint.config.mjs
  • src/cartesian/Area.tsx
  • src/cartesian/Bar.tsx
  • src/chart/Sankey.tsx
  • src/component/LabelList.tsx
  • src/component/Text.tsx
  • src/polar/Pie.tsx
  • src/polar/RadialBar.tsx
  • src/state/chartDataSlice.ts
  • src/state/selectors/areaSelectors.ts
  • src/state/types/AreaSettings.ts
  • src/util/ChartUtils.ts
  • src/util/getTypedValue.ts
  • src/util/scale/getNiceTickValues.ts
  • src/util/scale/util/utils.ts
  • src/util/types.ts
  • test/cartesian/Area.spec.tsx
  • test/cartesian/Area.typed.spec.tsx
  • test/state/selectors/axisSelectors.spec.tsx
  • test/state/selectors/dataSelectors.spec.tsx
  • test/state/selectors/selectors.spec.tsx
  • test/util/ChartUtils/truncateByDomain.spec.ts
  • test/util/getTypedValue.spec.tsx
  • test/util/scale/utils.spec.ts
💤 Files with no reviewable changes (2)
  • src/util/scale/util/utils.ts
  • test/util/scale/utils.spec.ts
🧰 Additional context used
📓 Path-based instructions (6)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

**/*.{ts,tsx}: Never use any type (implicit or explicit) in TypeScript code
Prefer unknown over any and refine the type in TypeScript
Type function parameters and return values explicitly in TypeScript, do not rely on implicit any or inference; exceptions are React components and trivial functions
Do not use as type assertions in TypeScript; the only exception is as const

All imports from recharts must use the public API entry point (e.g., import { TooltipIndex } from 'recharts'). Imports from internal paths like recharts/types/* or recharts/src/* are not allowed and will fail the linter.

Files:

  • src/state/selectors/areaSelectors.ts
  • test/cartesian/Area.spec.tsx
  • src/util/types.ts
  • src/chart/Sankey.tsx
  • test/cartesian/Area.typed.spec.tsx
  • test/state/selectors/dataSelectors.spec.tsx
  • test/util/ChartUtils/truncateByDomain.spec.ts
  • src/polar/RadialBar.tsx
  • src/util/getTypedValue.ts
  • src/cartesian/Bar.tsx
  • src/state/types/AreaSettings.ts
  • test/state/selectors/selectors.spec.tsx
  • src/component/Text.tsx
  • test/state/selectors/axisSelectors.spec.tsx
  • src/state/chartDataSlice.ts
  • src/util/scale/getNiceTickValues.ts
  • src/component/LabelList.tsx
  • src/cartesian/Area.tsx
  • src/util/ChartUtils.ts
  • src/polar/Pie.tsx
  • test/util/getTypedValue.spec.tsx
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Ensure code lints by running npm run lint and follows Airbnb's Style Guide

Files:

  • src/state/selectors/areaSelectors.ts
  • test/cartesian/Area.spec.tsx
  • src/util/types.ts
  • src/chart/Sankey.tsx
  • test/cartesian/Area.typed.spec.tsx
  • test/state/selectors/dataSelectors.spec.tsx
  • test/util/ChartUtils/truncateByDomain.spec.ts
  • src/polar/RadialBar.tsx
  • src/util/getTypedValue.ts
  • src/cartesian/Bar.tsx
  • src/state/types/AreaSettings.ts
  • test/state/selectors/selectors.spec.tsx
  • src/component/Text.tsx
  • test/state/selectors/axisSelectors.spec.tsx
  • src/state/chartDataSlice.ts
  • src/util/scale/getNiceTickValues.ts
  • src/component/LabelList.tsx
  • src/cartesian/Area.tsx
  • src/util/ChartUtils.ts
  • src/polar/Pie.tsx
  • test/util/getTypedValue.spec.tsx
src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Do not hardcode any strings or formatting choices in library code; users should provide localized strings as desired

Files:

  • src/state/selectors/areaSelectors.ts
  • src/util/types.ts
  • src/chart/Sankey.tsx
  • src/polar/RadialBar.tsx
  • src/util/getTypedValue.ts
  • src/cartesian/Bar.tsx
  • src/state/types/AreaSettings.ts
  • src/component/Text.tsx
  • src/state/chartDataSlice.ts
  • src/util/scale/getNiceTickValues.ts
  • src/component/LabelList.tsx
  • src/cartesian/Area.tsx
  • src/util/ChartUtils.ts
  • src/polar/Pie.tsx
test/**/*.spec.{ts,tsx}

📄 CodeRabbit inference engine (CONTRIBUTING.md)

Aim for 100% unit test code coverage when writing new code

Files:

  • test/cartesian/Area.spec.tsx
  • test/cartesian/Area.typed.spec.tsx
  • test/state/selectors/dataSelectors.spec.tsx
  • test/util/ChartUtils/truncateByDomain.spec.ts
  • test/state/selectors/selectors.spec.tsx
  • test/state/selectors/axisSelectors.spec.tsx
  • test/util/getTypedValue.spec.tsx
test/**/*.{test,spec}.{ts,tsx}

📄 CodeRabbit inference engine (test/README.md)

test/**/*.{test,spec}.{ts,tsx}: Aim for 100% unit test code coverage when writing new code
Prefer to use the createSelectorTestCase helper function when writing or modifying tests
Use the expectLastCalledWith helper function instead of expect(spy).toHaveBeenLastCalledWith(...) for better typing and autocompletion
Verify the number of selector calls using the spy object from createSelectorTestCase to spot unnecessary re-renders and improve performance
Mock getBoundingClientRect in tests using the helper function provided in test/helper/MockGetBoundingClientRect.ts
Use vi.useFakeTimers() in all tests due to Redux autoBatchEnhancer dependency on timers and requestAnimationFrame
Call vi.runOnlyPendingTimers() to advance timers after renders when not using createSelectorTestCase helper, and avoid vi.runAllTimers() to prevent infinite loops
Use userEvent.setup({ advanceTimers: vi.runOnlyPendingTimers }) or the userEventSetup helper function from test/helper/userEventSetup.ts when creating userEvent instances
When testing tooltips on hover, use vi.runOnlyPendingTimers() after each userEvent.hover() call or use the showTooltip helper function from tooltipTestHelpers.ts to account for requestAnimationFrame delays

Files:

  • test/cartesian/Area.spec.tsx
  • test/cartesian/Area.typed.spec.tsx
  • test/state/selectors/dataSelectors.spec.tsx
  • test/util/ChartUtils/truncateByDomain.spec.ts
  • test/state/selectors/selectors.spec.tsx
  • test/state/selectors/axisSelectors.spec.tsx
  • test/util/getTypedValue.spec.tsx
**/*.spec.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

When running unit tests, prefer to run a single test file using npm run test -- path/to/TestFile.spec.tsx rather than running all tests with npm test

Unit tests should be placed in the test directory, with some tests also allowed in www/test. Test files follow the naming convention *.spec.tsx.

Files:

  • test/cartesian/Area.spec.tsx
  • test/cartesian/Area.typed.spec.tsx
  • test/state/selectors/dataSelectors.spec.tsx
  • test/util/ChartUtils/truncateByDomain.spec.ts
  • test/state/selectors/selectors.spec.tsx
  • test/state/selectors/axisSelectors.spec.tsx
  • test/util/getTypedValue.spec.tsx
🧠 Learnings (30)
📓 Common learnings
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6750
File: src/state/selectors/axisSelectors.ts:593-602
Timestamp: 2025-12-08T08:23:26.261Z
Learning: In the recharts codebase, `DataKey<any>` is an intentional exception to the "no any" rule while proper typing is being developed. This should not be flagged in reviews.
📚 Learning: 2025-12-16T08:12:06.809Z
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6783
File: test/util/ChartUtils.spec.tsx:15-16
Timestamp: 2025-12-16T08:12:06.809Z
Learning: In tests (files under any test directory, e.g., test/, __tests__/, etc.), allow importing internal module paths (e.g., ../../src/...) and do not require imports to use the public API entry point (src/index.ts). The public API import restriction should apply only to non-test code. During reviews, accept internal imports in test files and enforce the public API pattern only for non-test code.

Applied to files:

  • test/cartesian/Area.spec.tsx
  • test/cartesian/Area.typed.spec.tsx
  • test/state/selectors/dataSelectors.spec.tsx
  • test/state/selectors/selectors.spec.tsx
  • test/state/selectors/axisSelectors.spec.tsx
  • test/util/getTypedValue.spec.tsx
📚 Learning: 2025-11-25T01:23:08.250Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: test/README.md:0-0
Timestamp: 2025-11-25T01:23:08.250Z
Learning: Applies to test/**/*.{test,spec}.{ts,tsx} : Verify the number of selector calls using the spy object from `createSelectorTestCase` to spot unnecessary re-renders and improve performance

Applied to files:

  • test/cartesian/Area.spec.tsx
  • test/cartesian/Area.typed.spec.tsx
  • test/state/selectors/dataSelectors.spec.tsx
  • test/state/selectors/selectors.spec.tsx
  • test/state/selectors/axisSelectors.spec.tsx
  • test/util/getTypedValue.spec.tsx
📚 Learning: 2025-12-26T15:59:11.254Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: DEVELOPING.md:0-0
Timestamp: 2025-12-26T15:59:11.254Z
Learning: Applies to **/*.spec.{ts,tsx} : Unit tests should be placed in the `test` directory, with some tests also allowed in `www/test`. Test files follow the naming convention `*.spec.tsx`.

Applied to files:

  • test/cartesian/Area.spec.tsx
  • test/cartesian/Area.typed.spec.tsx
  • test/util/getTypedValue.spec.tsx
📚 Learning: 2025-11-25T01:23:08.250Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: test/README.md:0-0
Timestamp: 2025-11-25T01:23:08.250Z
Learning: Applies to test/**/*.{test,spec}.{ts,tsx} : Mock `getBoundingClientRect` in tests using the helper function provided in `test/helper/MockGetBoundingClientRect.ts`

Applied to files:

  • test/cartesian/Area.spec.tsx
  • test/util/getTypedValue.spec.tsx
📚 Learning: 2025-11-25T01:22:59.729Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-25T01:22:59.729Z
Learning: Applies to storybook/stories/**/*.stories.tsx : Update Storybook stories when APIs have been changed to ensure they work as expected

Applied to files:

  • test/cartesian/Area.spec.tsx
  • test/state/selectors/selectors.spec.tsx
📚 Learning: 2025-11-25T01:23:08.250Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: test/README.md:0-0
Timestamp: 2025-11-25T01:23:08.250Z
Learning: Applies to test/**/*.{test,spec}.{ts,tsx} : Use `vi.useFakeTimers()` in all tests due to Redux autoBatchEnhancer dependency on timers and `requestAnimationFrame`

Applied to files:

  • test/cartesian/Area.spec.tsx
  • test/state/selectors/dataSelectors.spec.tsx
  • test/state/selectors/selectors.spec.tsx
  • test/util/getTypedValue.spec.tsx
📚 Learning: 2026-01-06T22:33:55.414Z
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6855
File: www/src/docs/api/AreaChartAPI.tsx:422-438
Timestamp: 2026-01-06T22:33:55.414Z
Learning: In Recharts 3, components can render inside any chart type as long as the parent provides the necessary context. For example, Funnel can render inside AreaChart, BarChart, ComposedChart, LineChart, and ScatterChart because they all provide cartesian context. This is a change from Recharts 2, which had stricter parent-child component limitations.

Applied to files:

  • test/cartesian/Area.spec.tsx
  • test/cartesian/Area.typed.spec.tsx
  • test/state/selectors/dataSelectors.spec.tsx
  • test/state/selectors/selectors.spec.tsx
  • test/state/selectors/axisSelectors.spec.tsx
  • src/cartesian/Area.tsx
📚 Learning: 2025-12-08T08:23:26.261Z
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6750
File: src/state/selectors/axisSelectors.ts:593-602
Timestamp: 2025-12-08T08:23:26.261Z
Learning: In the recharts codebase, `DataKey<any>` is an intentional exception to the "no any" rule while proper typing is being developed. This should not be flagged in reviews.

Applied to files:

  • src/util/types.ts
  • test/cartesian/Area.typed.spec.tsx
  • src/cartesian/Bar.tsx
  • src/state/types/AreaSettings.ts
  • src/state/chartDataSlice.ts
  • src/cartesian/Area.tsx
  • src/util/ChartUtils.ts
  • test/util/getTypedValue.spec.tsx
📚 Learning: 2025-11-23T13:30:10.395Z
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6669
File: www/src/docs/exampleComponents/ScatterChart/ScatterChartWithLabels.tsx:2-2
Timestamp: 2025-11-23T13:30:10.395Z
Learning: The `TooltipIndex` type from recharts is defined in `src/state/tooltipSlice.ts` but is not currently exported from the public API entry points. It should not be imported from `recharts/types/state/tooltipSlice` as this is an internal implementation path. An ESLint rule is needed to prevent regressions.

Applied to files:

  • src/chart/Sankey.tsx
  • test/state/selectors/dataSelectors.spec.tsx
  • src/state/chartDataSlice.ts
  • src/polar/Pie.tsx
📚 Learning: 2025-12-14T13:58:49.197Z
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6771
File: src/shape/Curve.tsx:39-40
Timestamp: 2025-12-14T13:58:49.197Z
Learning: In the recharts codebase, hooks like useAppSelector and other hooks (e.g., useChartLayout()) return undefined when used outside Redux Provider context, instead of throwing. This enables components that call these hooks, such as Curve, to operate in standalone mode by falling back to prop values. During code reviews, ensure TSX files gracefully handle undefined results from hooks and implement fallbacks (e.g., via default props or explicit prop-based values) rather than assuming the hook is always within Provider.

Applied to files:

  • src/chart/Sankey.tsx
  • src/polar/RadialBar.tsx
  • src/cartesian/Bar.tsx
  • src/component/Text.tsx
  • src/component/LabelList.tsx
  • src/cartesian/Area.tsx
  • src/polar/Pie.tsx
📚 Learning: 2025-11-25T01:22:59.729Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-25T01:22:59.729Z
Learning: Applies to test/component/**/*.spec.tsx : Use React Testing Library for testing component interactions and behavior upon rendering

Applied to files:

  • test/cartesian/Area.typed.spec.tsx
  • test/state/selectors/dataSelectors.spec.tsx
  • src/cartesian/Area.tsx
📚 Learning: 2025-12-26T15:59:11.254Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: DEVELOPING.md:0-0
Timestamp: 2025-12-26T15:59:11.254Z
Learning: Applies to **/*.{ts,tsx} : All imports from `recharts` must use the public API entry point (e.g., `import { TooltipIndex } from 'recharts'`). Imports from internal paths like `recharts/types/*` or `recharts/src/*` are not allowed and will fail the linter.

Applied to files:

  • test/cartesian/Area.typed.spec.tsx
  • test/state/selectors/dataSelectors.spec.tsx
  • src/cartesian/Area.tsx
📚 Learning: 2025-12-26T15:59:11.254Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: DEVELOPING.md:0-0
Timestamp: 2025-12-26T15:59:11.254Z
Learning: Applies to test-vr/**/*.spec.ts : Visual regression tests should follow the naming convention `*.spec.ts` and be placed in the `test-vr` directory. When updating snapshots, new files created in `test-vr/__snapshots__` should be committed to the repository.

Applied to files:

  • test/cartesian/Area.typed.spec.tsx
📚 Learning: 2025-11-25T01:23:08.250Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: test/README.md:0-0
Timestamp: 2025-11-25T01:23:08.250Z
Learning: Applies to test/**/*.{test,spec}.{ts,tsx} : Use the `expectLastCalledWith` helper function instead of `expect(spy).toHaveBeenLastCalledWith(...)` for better typing and autocompletion

Applied to files:

  • test/cartesian/Area.typed.spec.tsx
  • test/state/selectors/selectors.spec.tsx
  • test/state/selectors/axisSelectors.spec.tsx
  • test/util/getTypedValue.spec.tsx
📚 Learning: 2025-11-25T01:22:59.729Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-25T01:22:59.729Z
Learning: Applies to test/**/*.spec.{ts,tsx} : Aim for 100% unit test code coverage when writing new code

Applied to files:

  • test/cartesian/Area.typed.spec.tsx
  • test/util/getTypedValue.spec.tsx
📚 Learning: 2025-12-26T15:59:11.254Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: DEVELOPING.md:0-0
Timestamp: 2025-12-26T15:59:11.254Z
Learning: For release testing, use `yalc` to test Recharts changes in a test package as close as possible to the results of `npm publish` to ensure no unintended breaking changes are published.

Applied to files:

  • test/cartesian/Area.typed.spec.tsx
📚 Learning: 2025-11-25T01:22:59.729Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-25T01:22:59.729Z
Learning: Applies to **/*.{ts,tsx} : Type function parameters and return values explicitly in TypeScript, do not rely on implicit any or inference; exceptions are React components and trivial functions

Applied to files:

  • test/cartesian/Area.typed.spec.tsx
  • src/cartesian/Area.tsx
  • test/util/getTypedValue.spec.tsx
  • eslint.config.mjs
📚 Learning: 2025-11-25T01:23:08.250Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: test/README.md:0-0
Timestamp: 2025-11-25T01:23:08.250Z
Learning: Applies to test/**/*.{test,spec}.{ts,tsx} : Prefer to use the `createSelectorTestCase` helper function when writing or modifying tests

Applied to files:

  • test/cartesian/Area.typed.spec.tsx
  • test/state/selectors/dataSelectors.spec.tsx
  • test/state/selectors/selectors.spec.tsx
  • test/state/selectors/axisSelectors.spec.tsx
  • test/util/getTypedValue.spec.tsx
📚 Learning: 2025-12-14T13:58:58.361Z
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6771
File: src/shape/Curve.tsx:39-40
Timestamp: 2025-12-14T13:58:58.361Z
Learning: In the recharts codebase, `useAppSelector` and hooks like `useChartLayout()` are designed to return `undefined` when used outside Redux Provider context, rather than throwing errors. This allows components like `Curve` that call these hooks to work standalone by falling back to prop values.

Applied to files:

  • test/state/selectors/dataSelectors.spec.tsx
  • test/state/selectors/axisSelectors.spec.tsx
📚 Learning: 2025-11-25T01:22:59.729Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-25T01:22:59.729Z
Learning: Applies to storybook/stories/**/*.stories.tsx : Use Storybook for smoke tests and add play functions with assertions for actual tests

Applied to files:

  • test/state/selectors/dataSelectors.spec.tsx
📚 Learning: 2025-11-25T01:23:08.250Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: test/README.md:0-0
Timestamp: 2025-11-25T01:23:08.250Z
Learning: Applies to test/**/*.{test,spec}.{ts,tsx} : Call `vi.runOnlyPendingTimers()` to advance timers after renders when not using `createSelectorTestCase` helper, and avoid `vi.runAllTimers()` to prevent infinite loops

Applied to files:

  • test/state/selectors/dataSelectors.spec.tsx
  • test/state/selectors/axisSelectors.spec.tsx
📚 Learning: 2025-12-26T15:59:11.254Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: DEVELOPING.md:0-0
Timestamp: 2025-12-26T15:59:11.254Z
Learning: Applies to **/*.stories.{ts,tsx} : When adding new Storybook stories, prioritize high-fidelity examples that you want published on the website and in the Storybook UI. Use low-fidelity tests in unit tests or visual regression tests instead to avoid exceeding the Chromatic open source plan limits.

Applied to files:

  • test/state/selectors/dataSelectors.spec.tsx
📚 Learning: 2025-12-16T08:12:13.355Z
Learnt from: PavelVanecek
Repo: recharts/recharts PR: 6783
File: test/util/ChartUtils.spec.tsx:15-16
Timestamp: 2025-12-16T08:12:13.355Z
Learning: In the recharts codebase, files in the `test` folder are allowed to import from internal paths (e.g., `../../src/state/cartesianAxisSlice`) and do not need to use the public API entry point (`src/index.ts`). The public API import restriction applies only to non-test code.

Applied to files:

  • test/util/ChartUtils/truncateByDomain.spec.ts
  • src/cartesian/Bar.tsx
  • src/cartesian/Area.tsx
📚 Learning: 2025-11-25T01:23:08.250Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: test/README.md:0-0
Timestamp: 2025-11-25T01:23:08.250Z
Learning: Applies to test/**/*.{test,spec}.{ts,tsx} : Aim for 100% unit test code coverage when writing new code

Applied to files:

  • test/util/getTypedValue.spec.tsx
📚 Learning: 2025-11-25T01:22:59.729Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-25T01:22:59.729Z
Learning: Applies to **/*.{ts,tsx} : Never use `any` type (implicit or explicit) in TypeScript code

Applied to files:

  • eslint.config.mjs
📚 Learning: 2025-11-25T01:22:59.729Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-25T01:22:59.729Z
Learning: Applies to **/*.{js,ts,tsx} : Ensure code lints by running `npm run lint` and follows Airbnb's Style Guide

Applied to files:

  • eslint.config.mjs
📚 Learning: 2025-11-25T01:22:59.729Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-25T01:22:59.729Z
Learning: Applies to **/*.{ts,tsx} : Prefer `unknown` over `any` and refine the type in TypeScript

Applied to files:

  • eslint.config.mjs
📚 Learning: 2025-11-25T01:22:59.729Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: CONTRIBUTING.md:0-0
Timestamp: 2025-11-25T01:22:59.729Z
Learning: Applies to **/*.{ts,tsx} : Do not use `as` type assertions in TypeScript; the only exception is `as const`

Applied to files:

  • eslint.config.mjs
📚 Learning: 2025-12-26T15:59:11.254Z
Learnt from: CR
Repo: recharts/recharts PR: 0
File: DEVELOPING.md:0-0
Timestamp: 2025-12-26T15:59:11.254Z
Learning: Enable ESLint and Prettier configuration in your IDE for consistent development experience.

Applied to files:

  • eslint.config.mjs
🧬 Code graph analysis (17)
src/util/types.ts (1)
src/util/getTypedValue.ts (1)
  • TypedDataKey (8-20)
src/chart/Sankey.tsx (1)
src/util/getTypedValue.ts (1)
  • getTypedValue (49-67)
test/cartesian/Area.typed.spec.tsx (3)
test/helper/expectAreaCurve.ts (2)
  • ExpectedArea (4-6)
  • expectAreaCurve (8-14)
test/helper/createSelectorTestCase.tsx (1)
  • rechartsTestRender (172-184)
src/cartesian/Area.tsx (1)
  • Area (1111-1113)
test/state/selectors/dataSelectors.spec.tsx (1)
test/_data.ts (1)
  • PageData (4-11)
test/util/ChartUtils/truncateByDomain.spec.ts (2)
src/util/ChartUtils.ts (1)
  • truncateByDomain (296-325)
test/cartesian/ReferenceLine/getEndPoints.spec.tsx (1)
  • domain (9-11)
src/polar/RadialBar.tsx (2)
src/util/ChartUtils.ts (1)
  • truncateByDomain (296-325)
src/util/getTypedValue.ts (1)
  • getTypedValue (49-67)
src/util/getTypedValue.ts (1)
src/util/DataUtils.ts (2)
  • isNullish (180-182)
  • isNumOrStr (27-27)
src/cartesian/Bar.tsx (2)
src/util/ChartUtils.ts (2)
  • getBaseValueOfBar (565-585)
  • truncateByDomain (296-325)
src/util/getTypedValue.ts (1)
  • getTypedValue (49-67)
src/state/types/AreaSettings.ts (4)
src/state/graphicalItemsSlice.ts (1)
  • BaseCartesianGraphicalItemSettings (43-57)
src/state/types/StackedGraphicalItem.ts (1)
  • MaybeStackedGraphicalItem (9-15)
src/cartesian/Area.tsx (1)
  • BaseValue (64-64)
src/util/getTypedValue.ts (1)
  • TypedDataKey (8-20)
test/state/selectors/selectors.spec.tsx (2)
src/cartesian/Area.tsx (2)
  • render (777-830)
  • Area (1111-1113)
test/_data.ts (1)
  • PageData (4-11)
src/component/Text.tsx (2)
src/index.ts (1)
  • RenderableText (28-28)
src/util/DataUtils.ts (1)
  • isNullish (180-182)
test/state/selectors/axisSelectors.spec.tsx (2)
src/cartesian/Area.tsx (1)
  • Area (1111-1113)
src/index.ts (1)
  • Area (88-88)
src/util/scale/getNiceTickValues.ts (1)
storybook/stories/data/Tick.ts (1)
  • ticks (16-16)
src/cartesian/Area.tsx (2)
src/util/getTypedValue.ts (2)
  • TypedDataKey (8-20)
  • getTypedValue (49-67)
src/state/chartDataSlice.ts (1)
  • ChartData (15-15)
src/util/ChartUtils.ts (2)
test/cartesian/ReferenceLine/getEndPoints.spec.tsx (1)
  • domain (9-11)
src/util/DataUtils.ts (1)
  • isNumber (24-25)
src/polar/Pie.tsx (2)
src/util/getTypedValue.ts (1)
  • getTypedValue (49-67)
storybook/stories/API/props/CartesianComponentShared.ts (1)
  • dataKey (3-10)
test/util/getTypedValue.spec.tsx (1)
src/util/getTypedValue.ts (2)
  • TypedDataKey (8-20)
  • getTypedValue (49-67)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Build, Test, Pack
🔇 Additional comments (32)
src/state/selectors/areaSelectors.ts (1)

27-27: Type change appropriately widens value property.

The change from [number, number] to ReadonlyArray<unknown> aligns with the PR's typed data access goals, enabling more flexible value extraction while preserving readonly semantics.

test/state/selectors/dataSelectors.spec.tsx (1)

54-59: Test data correctly updated for typed dataKey pattern.

The change from primitive arrays to object arrays with explicit dataKey props properly demonstrates the typed data access pattern introduced in this PR.

src/state/chartDataSlice.ts (1)

15-15: Generic type parameter enhances type safety while maintaining backward compatibility.

The addition of the generic parameter with default T = unknown enables better type inference for consumers while preserving all existing usage patterns.

test/state/selectors/axisSelectors.spec.tsx (1)

170-170: Test data systematically updated for typed dataKey pattern.

All Area components have been consistently updated to use object-based data with explicit dataKey props, properly testing the new typed data access pattern while maintaining comprehensive coverage of axis selector behavior.

Also applies to: 235-235, 470-470, 1414-1415, 1464-1464

test/util/ChartUtils/truncateByDomain.spec.ts (1)

13-17: Test expectations correctly updated for new return type.

The changes properly verify that truncateByDomain now returns undefined for invalid or NaN domains, aligning with the updated function signature [number, number] | undefined.

Also applies to: 56-60

test/state/selectors/selectors.spec.tsx (1)

1230-1249: LGTM! Test data extraction improves readability.

Extracting the inline data arrays into named variables data1 and data2 makes the test more readable and maintainable. The test logic remains unchanged.

src/util/types.ts (1)

37-37: LGTM! Core type alias for typed data access.

The change from the previous DataKey<T> definition to aliasing TypedDataKey<T> is the foundation for the typed data key functionality described in the PR objectives. This enables TypeScript to enforce the relationship between data and dataKey within components.

Also applies to: 94-94

eslint.config.mjs (1)

473-486: LGTM! Strict typing enforcement for scale utilities.

The new neverAny ESLint configuration enforces stricter TypeScript rules for the src/util/scale/** directory. This prevents the use of any, TypeScript comment pragmas, and type assertions, ensuring proper typing in utility code.

As per coding guidelines, this aligns with the requirement to avoid any types in TypeScript code.

Also applies to: 515-515

src/polar/Pie.tsx (1)

66-66: LGTM! Adoption of typed data value extraction.

The change from getValueByDataKey to getTypedValue aligns with the PR's systematic replacement of data extraction utilities with the new typed approach. This maintains the same runtime behavior while enabling better TypeScript support for label rendering.

Also applies to: 607-607

test/cartesian/Area.spec.tsx (1)

652-652: LGTM! Test data aligned with typed data access pattern.

The test data structure has been updated from primitive arrays [1, 2, 3] to object arrays [{ value: 1 }, { value: 2 }, { value: 3 }], which is necessary for the dataKey="value" extraction to work correctly with the new typed data access approach. Both occurrences are updated consistently.

Also applies to: 704-704

src/state/types/AreaSettings.ts (1)

5-5: LGTM! Type upgrade aligns with PR objectives.

The change from DataKey<any> to TypedDataKey implements the stricter typing described in the PR. This enables IDE autocomplete and type safety for the dataKey property while maintaining backward compatibility.

Also applies to: 12-12

src/polar/RadialBar.tsx (1)

645-645: LGTM! Data extraction updated to use typed utility.

The replacement of getValueByDataKey with getTypedValue aligns with the PR's goal of introducing stricter typing while maintaining functional equivalence.

src/component/Text.tsx (1)

77-79: LGTM! Runtime type guard complements compile-time types.

The new isRenderableText guard correctly validates values against the RenderableText type, enabling safer runtime checks in rendering paths. The implementation properly covers all union members: nullish values (via isNullish) and string/number/boolean primitives.

test/util/getTypedValue.spec.tsx (1)

1-235: LGTM! Comprehensive test coverage for new utility.

The test suite thoroughly covers getTypedValue functionality:

  • Object and array data access (string/numeric keys, nested paths)
  • Default value fallbacks
  • Edge cases (undefined, null, NaN)
  • Function-based accessors
  • Compile-time type inference validation

The type inference tests are particularly valuable for verifying TypeScript behavior with TypedDataKey.

src/cartesian/Bar.tsx (2)

89-89: LGTM!

Import of getTypedValue is correct and aligns with the PR's goal of migrating to typed data access.


987-1014: Type-safe data extraction and null-safety improvements look good.

The changes properly:

  1. Update stackedDomain to ReadonlyArray<unknown> | null to match the updated truncateByDomain signature
  2. Add explicit null check after truncateByDomain (lines 1002-1004) since it now returns undefined for invalid domains
  3. Use getTypedValue for raw value extraction in the non-stacked path

One consideration: value[1] on line 1016 is now unknown rather than a number. The minPointSizeCallback includes explicit runtime validation—it checks that the value is numeric or null/undefined and throws an invariant assertion if not. This validation ensures correctness at runtime, though TypeScript loses type information at the call site.

test/cartesian/Area.typed.spec.tsx (4)

1-4: LGTM!

Imports are correct. The test file properly uses the public API entry point for Area and AreaChart as required by coding guidelines, while internal test helpers are imported from relative paths (which is allowed for test files per learnings).


22-40: Well-structured backward compatibility tests.

The test suite appropriately documents the limitations of TypeScript's contextual typing—when data is on AreaChart and dataKey is on Area, type checking cannot connect them. The comments clearly explain this is expected behavior for backward compatibility.


132-148: Good use of @ts-expect-error for compile-time validation.

The tests at lines 140-143 correctly use @ts-expect-error to verify that TypeScript catches mismatched dataKeys when data is defined directly on Area. This validates the PR's typing improvements work as intended.


151-240: Comprehensive explicit type parameter tests.

The tests for <Area<ExampleDataPoint>> and <Area<ExampleDataPoint, number>> validate the new generic type parameters work correctly. The @ts-expect-error assertions confirm TypeScript catches type mismatches at compile time.

src/util/ChartUtils.ts (2)

44-46: Good deprecation notice.

The @deprecated JSDoc appropriately guides users to the new getTypedValue utility. This supports the gradual migration path mentioned in the PR objectives.


296-324: Breaking change in truncateByDomain return type correctly implemented.

The function now returns undefined instead of the original value when the domain is invalid. This is a safer approach that forces callers to handle the error case explicitly.

The changes are:

  1. domain parameter now accepts ReadonlyArray<unknown> | null | undefined
  2. Return type changed to [number, number] | undefined
  3. Invalid domain returns undefined (line 301)

All callers have been updated to handle the new return type:

  • src/cartesian/Bar.tsx:1002 checks if (truncated == null) before using the value
  • src/polar/RadialBar.tsx:640 checks if (truncated == null) before using the value
src/component/LabelList.tsx (4)

11-12: LGTM!

Imports correctly bring in the new typed utilities for value extraction and renderability checking.


19-19: Broadening value type to unknown is appropriate.

This change acknowledges that the value can come from arbitrary user data. The runtime isRenderableText checks (lines 155, 197) ensure only valid text types are rendered. This is a safer pattern than assuming the type at compile time.


151-159: Defensive defaultAccessor implementation.

The accessor now properly:

  1. Handles array values by extracting the last element
  2. Validates renderability before returning
  3. Returns undefined for non-renderable values

This prevents runtime errors from attempting to render objects or other non-text types.


193-199: Type-safe value extraction with renderability guard.

The updated logic:

  1. Uses getTypedValue for typed data access when dataKey is provided
  2. Guards against non-renderable values with isRenderableText check
  3. Returns null early to skip rendering invalid values

This is a robust pattern that prevents rendering failures.

src/util/getTypedValue.ts (3)

4-20: Well-designed TypedDataKey type.

The conditional type handles multiple scenarios elegantly:

  1. Falls back to loose typing when T is any or unknown
  2. Supports legacy string-indexed types
  3. Provides strict key inference for well-typed objects
  4. Supports array index access via template literal `${number}`
  5. Allows function accessors

The use of any on line 8 is appropriate per the codebase's intentional exception for DataKey<any> while proper typing is being developed. Based on learnings, this should not be flagged.


22-48: Comprehensive function overloads for type inference.

The overloads cover all practical use cases:

  • Direct property access (keyof T)
  • Function-based extraction
  • Array index access
  • Generic fallback with explicit return type

This enables TypeScript to infer the correct return type based on how the function is called, improving IDE autocomplete and type safety.


49-67: Clean implementation with proper null-safety.

The implementation:

  1. Returns defaultValue for nullish inputs (defensive)
  2. Uses es-toolkit/compat/get for safe nested property access
  3. Invokes function keys directly
  4. Falls back gracefully for unsupported key types

This matches the behavior of the deprecated getValueByDataKey while providing better type information.

src/cartesian/Area.tsx (3)

15-16: LGTM!

Import changes correctly replace getValueByDataKey usage with getTypedValue and add TypedDataKey for the typed dataKey prop.


1000-1017: Type-safe value extraction in computeArea.

The changes properly:

  1. Type valueAsArray as ReadonlyArray<unknown> | undefined to match the typed approach
  2. Use getTypedValue(entry, dataKey) for extraction
  3. Handle array vs scalar values appropriately
  4. Update isBreakPoint calculation to use getTypedValue

This maintains the same runtime behavior while improving type awareness.


1111-1115: Type assertion for generic memoized component.

The as assertion on the memoized component is necessary because React.memo doesn't preserve generic type parameters. This pattern is standard for generic React components that need memoization.

The @ts-expect-error on line 1114 for displayName is acceptable since TypeScript doesn't understand that the cast type can have additional properties.

Comment thread src/polar/RadialBar.tsx
Comment on lines +635 to +643
const untruncatedValue = stackedData[index + dataStartIndex];
if (untruncatedValue == null) {
return null;
}
const truncated = truncateByDomain(untruncatedValue, stackedDomain);
if (truncated == null) {
return null;
}
value = truncated;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Search for all usages of computeRadialBarDataItems
echo "=== Usages of computeRadialBarDataItems ==="
rg -n -C5 'computeRadialBarDataItems' src/

# Search for selectRadialBarSectors implementation
echo -e "\n=== selectRadialBarSectors definition ==="
rg -n -A20 'selectRadialBarSectors\s*=' src/

# Search for how sectors is used in RadialBarSectors
echo -e "\n=== RadialBarSectors component ==="
rg -n -B5 -A15 'const RadialBarSectors' src/

Repository: recharts/recharts

Length of output: 2742


🏁 Script executed:

#!/bin/bash
# Get the selectRadialBarSectors function
echo "=== selectRadialBarSectors function ==="
rg -n 'selectRadialBarSectors' src/state/selectors/radialBarSelectors.ts -A 30

# Get RadialBarSectors component
echo -e "\n=== RadialBarSectors component ==="
rg -n 'function RadialBarSectors|const RadialBarSectors' src/polar/RadialBar.tsx -A 30

# Check how the sectors data is used - look for .map on sectors
echo -e "\n=== Sectors usage in rendering ==="
rg -n 'sectors\.' src/polar/RadialBar.tsx -B2 -A2

Repository: recharts/recharts

Length of output: 3611


🏁 Script executed:

#!/bin/bash
# Get the full selectRadialBarSectors selector implementation
rg -n 'selectRadialBarSectors' src/state/selectors/radialBarSelectors.ts -A 60 | head -80

Repository: recharts/recharts

Length of output: 2430


🏁 Script executed:

#!/bin/bash
# Read the specific section of the file
sed -n '414,450p' src/state/selectors/radialBarSelectors.ts

Repository: recharts/recharts

Length of output: 979


🏁 Script executed:

#!/bin/bash
# Get the function signature and return statement
sed -n '581,650p' src/polar/RadialBar.tsx | head -80

Repository: recharts/recharts

Length of output: 1869


🏁 Script executed:

#!/bin/bash
# Get the complete function signature
sed -n '581,625p' src/polar/RadialBar.tsx

Repository: recharts/recharts

Length of output: 1153


Fix return type annotation and add null filtering to selectRadialBarSectors.

The function computeRadialBarDataItems now returns null values from its map callback (lines 635-643) but the return type is still declared as ReadonlyArray<RadialBarDataItem>, creating a type safety violation.

The selector selectRadialBarSectors (src/state/selectors/radialBarSelectors.ts:395) directly returns this result without filtering nulls. Consumers in RadialBarSectors (line 139) and RadialBarLabelListProvider (line 91-92) iterate the array with non-null type assertions and will fail at runtime if null entries are present.

Update the return type of computeRadialBarDataItems to ReadonlyArray<RadialBarDataItem | null>, and add .filter(Boolean) in selectRadialBarSectors to remove null entries before returning.

🤖 Prompt for AI Agents
In @src/polar/RadialBar.tsx around lines 635 - 643, computeRadialBarDataItems
currently returns nulls from its map callback but is typed as
ReadonlyArray<RadialBarDataItem>; change its return type to
ReadonlyArray<RadialBarDataItem | null> (update the function signature for
computeRadialBarDataItems) so the type matches possible nulls, and update
selectRadialBarSectors to filter out nulls before returning (e.g., append
.filter(Boolean) to the array returned by selectRadialBarSectors) so consumers
like RadialBarSectors and RadialBarLabelListProvider receive only non-null
RadialBarDataItem entries.

Comment on lines 179 to 186
if (cormin === -Infinity || cormax === Infinity) {
const values =
const values: Array<number> =
cormax === Infinity
? [cormin, ...range(0, tickCount - 1).map(() => Infinity)]
: [...range(0, tickCount - 1).map(() => -Infinity), cormax];
? [cormin, ...Array(tickCount - 1).fill(Infinity)]
: [...Array(tickCount - 1).fill(-Infinity), cormax];

return min > max ? values.reverse() : values;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Potential runtime crash: Array(tickCount - 1) can throw for small/invalid tickCount

You already normalize via const count = Math.max(tickCount, 2) (Line 176), but this branch still uses tickCount. If a caller passes tickCount <= 0, this will now throw RangeError: Invalid array length. Use count for the filled portion (and optionally for the returned length) to keep the “>= 2 ticks” contract.

Proposed fix
   if (cormin === -Infinity || cormax === Infinity) {
-    const values: Array<number> =
+    const values: number[] =
       cormax === Infinity
-        ? [cormin, ...Array(tickCount - 1).fill(Infinity)]
-        : [...Array(tickCount - 1).fill(-Infinity), cormax];
+        ? [cormin, ...Array(count - 1).fill(Infinity)]
+        : [...Array(count - 1).fill(-Infinity), cormax];

     return min > max ? values.reverse() : values;
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (cormin === -Infinity || cormax === Infinity) {
const values =
const values: Array<number> =
cormax === Infinity
? [cormin, ...range(0, tickCount - 1).map(() => Infinity)]
: [...range(0, tickCount - 1).map(() => -Infinity), cormax];
? [cormin, ...Array(tickCount - 1).fill(Infinity)]
: [...Array(tickCount - 1).fill(-Infinity), cormax];
return min > max ? values.reverse() : values;
}
if (cormin === -Infinity || cormax === Infinity) {
const values: number[] =
cormax === Infinity
? [cormin, ...Array(count - 1).fill(Infinity)]
: [...Array(count - 1).fill(-Infinity), cormax];
return min > max ? values.reverse() : values;
}
🤖 Prompt for AI Agents
In @src/util/scale/getNiceTickValues.ts around lines 179 - 186, The branch
handling infinite cormin/cormax uses Array(tickCount - 1) which can throw if
tickCount <= 0; replace uses of tickCount with the already-normalized count
(from const count = Math.max(tickCount, 2)) so the filled arrays are built with
Array(count - 1) and the returned values respect the normalized tick count;
update the two spread constructions that currently use Array(tickCount - 1) to
use Array(count - 1) (keeping the existing cormin/cormax placement and the min >
max reverse logic).

Comment thread eslint.config.mjs
},
];

const neverAny = {

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

TypeScript does not have these settings so eslint has to fill that gap

Comment thread src/cartesian/Area.tsx Outdated
let isRange = false;

const points: ReadonlyArray<AreaPointItem> = displayedData.map((entry, index): AreaPointItem => {
let valueAsArray: [number, number] | undefined;

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

The [number, number] was a mistake - charts can have string domains on both axes and they work perfectly fine, so this should be unknowns. As long as we can feed it to the scale, everything is fair game.

Comment thread src/cartesian/Area.tsx Outdated
const value1 = valueAsArray?.[1] ?? null;

const isBreakPoint = value1 == null || (hasStack && !connectNulls && getValueByDataKey(entry, dataKey) == null);
const isBreakPoint = value1 == null || (hasStack && !connectNulls && getTypedValue(entry, dataKey) == null);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Usage is the same

Comment thread src/cartesian/Area.tsx
* @consumes CartesianChartContext
*/
export const Area: ComponentType<Props> = React.memo(AreaFn, propsAreEqual);
export const Area = React.memo(AreaFn, propsAreEqual) as <DataPointType = any, ValueAxisType = any>(

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Defaults to any to keep existing behavior.

Comment thread src/util/getTypedValue.ts

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

I don't like this definition. If you can think of ways to improve it I am listening.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

whew thats a doozy

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This is probably the most interesting file to review @ckifer

*/
const { container } = rechartsTestRender(
<AreaChart data={data} width={400} height={400}>
<Area<ExampleDataPoint> dataKey="value" />

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

intellisense!
image

*/
const { container } = rechartsTestRender(
<AreaChart width={400} height={400}>
<Area data={data} dataKey={entry => entry.value} />

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Type hint shows correctly (no more any) though intellisense won't work
image

const { container } = rechartsTestRender(
<AreaChart data={data} width={400} height={400}>
{/* @ts-expect-error TypeScript is correct here - the dataKey does not match and this should be an error */}
<Area<ExampleDataPoint, number> dataKey="name" />

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

New (optional) syntax - it doesn't feel great.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

but better than not being possible

<ComposedChart data={PageData} width={100} height={100}>
<Area dataKey="" />
<Area dataKey="" data={[{ x: 10 }, { x: 20 }, { x: 30 }]} />
<Area dataKey="x" data={[{ x: 10 }, { x: 20 }, { x: 30 }]} />

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

This was incorrectly set up before (it didn't matter in the test) but TypeScript is highlighting it as a compile-time error now which I think is a great improvement!

@ckifer

ckifer commented Jan 13, 2026

Copy link
Copy Markdown
Member

Nice!

Also not the biggest fan of the generic component syntax but whatcha gonna do, this is a nice step forward and if people want to make their charts strictly typed they'll need to make them uglier.

We have to be careful not to break this once its in place though

@cloud-walker

Copy link
Copy Markdown
Contributor

Hi!, dunno if its late or if it can help, but on my product codebase I've baked this solution for a typesafe rechart version, taking advantage of the new features from recharts 3 + the good old render prop pattern.

This an example for ComposedChart:

import {token} from '@translated/babele-core'
import * as C from 'recharts'

import {ChartContainer} from '@/ui/Charts'

import {ChartArea} from './ChartArea'
import {ChartBar} from './ChartBar'
import {ChartTooltip} from './ChartTooltip'
import {ChartXAxis} from './ChartXAxis'
import {ChartYAxis} from './ChartYAxis'

export declare namespace ComposedChart {
  export interface Props<TPoint extends Record<string, unknown>> {
    /**
     * @todo cannot be readonly because of Recharts types
     */
    data: TPoint[]
    chartRef?: React.Ref<HTMLDivElement>
    render: (props: {
      Bar: typeof ChartBar<TPoint>
      Area: typeof ChartArea<TPoint>
      XAxis: typeof ChartXAxis<TPoint>
      YAxis: typeof ChartYAxis<TPoint>
      Tooltip: typeof ChartTooltip<TPoint>
    }) => React.ReactNode
  }
}

export function ComposedChart<TPoint extends Record<string, unknown>>(
  props: ComposedChart.Props<TPoint>,
) {
  return (
    <ChartContainer ref={props.chartRef}>
      <C.ComposedChart data={props.data}>
        <C.CartesianGrid
          vertical={false}
          strokeDasharray="4 4"
          stroke={token('colors.border.neutral.secondary')}
        />
        {props.render({
          Bar: ChartBar,
          Area: ChartArea,
          XAxis: ChartXAxis,
          YAxis: ChartYAxis,
          Tooltip: ChartTooltip,
        })}
      </C.ComposedChart>
    </ChartContainer>
  )
}

Then the usage becomes:

<ComposedChart
  chartRef={chartRef}
  data={points}
  render={({Bar, XAxis, Tooltip}) => {
    return (
      <>
        <XAxis
          dataKey="order_date"
          tickFormatter={(d) => formatChartDate(d, props.breakdownBy)} // d is of type Date!
        />
        <Bar dataKey="my_kpi" tone={kpiToTone.quality} /> // Bar knows which keys are available thanks to the render prop wiring
        <Tooltip
          tone={kpiToTone.quality}
          title={(p) => formatChartDate(p.order_date, props.breakdownBy)}
          mainRow={(p) => {
            if (p.ept_r1 == null) {
              return
            }
            return `${formatEPT(p.ept_r1)} EPT`
          }}
        />
      </>
    )
  }}
/>

I'm experimenting with this in my codebase, but I think its shaping in a good direction, its working good.

On the topic TkDodo recently published an article: Building Type-Safe Compound Components, that shares the same principle, but with a factory function instead of the render prop

@PavelVanecek

Copy link
Copy Markdown
Collaborator Author

Thanks @cloud-walker. I'm inclined towards the typed-function call (what the blog post calls "Component Factory Pattern") rather than renderProps because there's less runtime impact. I want to first test how it impacts bundle size and treeshaking though which is why I started with the explicit typing first.

ckifer pushed a commit that referenced this pull request Jan 26, 2026
## Description

Breaking down #6867 into
smaller PRs that we can merge easier. The types tend to spread
everywhere so I aim trying to isolate smaller chunks - this one is
adding a generic to `ChartData` and `DataProvider` types. These generics
are exposed to charts but

## Related Issue

#6645

## Types of changes

New feature I think?

- [ ] Bug fix (non-breaking change which fixes an issue)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing
functionality to change)

## Checklist:

I will add docs once it's done and ready for release

- [ ] My change requires a change to the documentation.
- [ ] I have updated the documentation accordingly.
- [ ] I have added tests to cover my changes.
- [ ] I have added a storybook story or VR test, or extended an existing
story or VR test to show my changes


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Refactor**
* Improved type safety across chart components by introducing generic
data-point typing and a standardized data-provider pattern for more
reliable data handling.
* **Stories & Tests**
* Strengthened example stories and tests with explicit data types and
runtime null-safety checks to reduce runtime errors and improve
developer confidence.

<sub>✏️ Tip: You can customize this high-level summary in your review
settings.</sub>
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
@PavelVanecek
PavelVanecek force-pushed the getTypedValue branch 2 times, most recently from 00dcef6 to 71b2129 Compare February 1, 2026 04:50
@PavelVanecek PavelVanecek mentioned this pull request Feb 7, 2026
5 tasks
@PavelVanecek
PavelVanecek deleted the getTypedValue branch March 6, 2026 13:43
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.

3 participants