Skip to content

blog: Table v9 type perf blog#979

Open
KevinVandy wants to merge 2 commits into
mainfrom
table-v9-type-perf-blog
Open

blog: Table v9 type perf blog#979
KevinVandy wants to merge 2 commits into
mainfrom
table-v9-type-perf-blog

Conversation

@KevinVandy

@KevinVandy KevinVandy commented Jun 13, 2026

Copy link
Copy Markdown
Member

Summary by CodeRabbit

  • Documentation
    • Added a comprehensive blog post covering TanStack Table V9 TypeScript type-checking performance enhancements, detailing optimization approaches and strategies employed throughout the library's development cycle.
    • Includes practical measurement guidance and best practices for developers to evaluate and improve type instantiation performance in their own applications.

@coderabbitai

coderabbitai Bot commented Jun 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds a new blog post documenting the TypeScript type-checking performance improvements made to TanStack Table V9 from alpha.54 through beta.11, detailing the shift from conditional unions to feature maps, optimization techniques, measurement methodology, and practical guidance for developers.

Changes

TypeScript Performance Optimization Blog Post

Layer / File(s) Summary
Post setup and technical context
src/blog/tanstack-table-v9-typescript-performance.md
Blog frontmatter, header image, and explanation of V9's type system complexity compared to V8 due to feature modularization and the TFeatures generic narrowing.
Feature map approach and initial performance metrics
src/blog/tanstack-table-v9-typescript-performance.md
Documents the transition from alpha's hand-written fourteen-branch conditional unions to a named feature map extraction pattern, and reports instantiation metric improvements from alpha.54 to beta.11.
Measurement methodology and results tables
src/blog/tanstack-table-v9-typescript-performance.md
Explains tsc diagnostic tools (--extendedDiagnostics, --generateTrace) and presents detailed instantiation results across @tanstack/table-core, adapters, and example projects.
Optimization mechanisms and strategies
src/blog/tanstack-table-v9-typescript-performance.md
Details four major optimizations: ExtractFeatureMapTypes for feature mapping, converting Table_Internal to an interface, adding in out variance annotations, and explicit type arguments in adapter helpers to eliminate inference overhead.
Practical guidance and takeaways
src/blog/tanstack-table-v9-typescript-performance.md
Provides four numbered recommendations for practitioners: measuring instantiations, understanding named-type cache behavior, careful variance annotation application, and tracking optimization failures.

🎯 1 (Trivial) | ⏱️ ~3 minutes

Suggested reviewers

  • schiller-manuel
  • AlemTuzlak

🐰 A post about types so sleek,
Performance peaks reached week by week!
Features mapped with care and grace,
Variance annotations find their place—
Instants drop, and editors zoom,
No more slowdown in the room!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'blog: Table v9 type perf blog' directly relates to the main change—adding a blog post about TanStack Table V9 TypeScript performance.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch table-v9-type-perf-blog

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/blog/tanstack-table-v9-typescript-performance.md`:
- Line 16: Fix the typo in the intro sentence: replace the phrase "turned our to
be one of our bigger optimizations" with "turned out to be one of our bigger
optimizations" so the sentence reads correctly.
- Around line 257-261: The paragraph overstates the safety of the `in out`
variance claim; replace the sentences that say "`in out` annotation is simply
trusted" and "sound to do regardless of the structure" with a more accurate
explanation: state that `in out` asserts invariance for the compiler's
instantiation-based comparisons and can bypass structural variance probing
(i.e., the compiler will use the annotated invariance rather than derive
variance from the type's structure), that this usually only restricts
assignability but can also remove relations the code relies on (as shown by
`TValue` breaking the build), and therefore it should be applied only where
parameters are invariant in practice (keeping the `Table_Internal` and `TValue`
examples as-is to illustrate the benefit and caveat).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0e6d1f5f-d5d0-48de-88f5-ab374867343d

📥 Commits

Reviewing files that changed from the base of the PR and between 19fa64a and c87762d.

⛔ Files ignored due to path filters (1)
  • public/blog-assets/tanstack-table-v9-typescript-performance/header.png is excluded by !**/*.png
📒 Files selected for processing (1)
  • src/blog/tanstack-table-v9-typescript-performance.md


If you had been using the Table V9 alphas, there's a chance that you could feel a bit of slowness in your editor. Good news, though! Between the alpha and the latest beta, we cut TypeScript's type-checking work by 66-85% across every package and example! The latest beta now type-checks faster than our alpha versions from last week by a wide margin, and the editor experience is back to feeling nearly instant.

This post covers where the cost came from, how we measured it, and the specific changes that fixed these issues. One of those changes is a still overlooked TypeScript feature that many library authors still seem to barely use, and it turned our to be one of our bigger optimizations, turning a trade-off into a win across the board.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Fix the typo in the introduction.

“turned our” should be “turned out.” Small, but this is front-matter copy and it’s immediately visible.

🧰 Tools
🪛 LanguageTool

[style] ~16-~16: Consider using a different verb for a more formal wording.
Context: ...sured it, and the specific changes that fixed these issues. One of those changes is a...

(FIX_RESOLVE)


[grammar] ~16-~16: Use a hyphen to join words.
Context: ... issues. One of those changes is a still overlooked TypeScript feature that many ...

(QB_NEW_EN_HYPHEN)


[grammar] ~16-~16: Ensure spelling is correct
Context: ...still seem to barely use, and it turned our to be one of our bigger optimizations, ...

(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/blog/tanstack-table-v9-typescript-performance.md` at line 16, Fix the
typo in the intro sentence: replace the phrase "turned our to be one of our
bigger optimizations" with "turned out to be one of our bigger optimizations" so
the sentence reads correctly.

Comment on lines +257 to +261
`in out` declares the parameter invariant, and it is not a cache. It changes no behavior here, because these parameters already appear in both input and output positions throughout, so they were invariant in practice anyway. One subtlety worth being precise about: unlike a lone `in` or `out`, which the compiler does check against the type's structure, an `in out` annotation is simply trusted. That is sound to do regardless of the structure, because invariance is the strictest relation there is. Asserting it can only remove assignments the compiler would otherwise have allowed, never permit an unsound one. What the annotation buys is the shortcut. The compiler relates instantiations by their type arguments directly, with no variance probing and no structural fallback.

Annotating `Table_Internal` alone took the react adapter from 136k down to 66k, below where it started. Annotating the rest of table-core's generic interfaces (166 parameters in total) took it to 54.7k, shaved another 3% off the core, and moved the kitchen-sink example from flat to 13% better. What had looked like a core-versus-adapters trade-off became a win everywhere, for the price of two keywords per type parameter.

One caveat from the same experiment, because variance annotations are not free everywhere. We tried annotating `TValue`, the cell value parameter, and the build broke immediately. `TValue` is genuinely covariant in output-position types, and forcing it invariant rejected the `Column<TF, TD, string>` to `Column<TF, TD, unknown>` widening that both the library and your apps rely on. The annotation itself was accepted, exactly as the "invariance is always safe" rule predicts. What it removed was a relation we actually needed, and the build caught it. So the rule of thumb is to only annotate parameters that are invariant in practice, and to treat a failing build as the check the annotation does not do for you. Measure, don't assume.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail
# Inspect the surrounding text to ensure the exact claim/wording is present.
sed -n '230,290p' src/blog/tanstack-table-v9-typescript-performance.md | nl -ba

Repository: TanStack/tanstack.com

Length of output: 108


🏁 Script executed:

#!/bin/bash
set -euo pipefail
file="src/blog/tanstack-table-v9-typescript-performance.md"
# Print the referenced region with line numbers (avoid nl).
awk -v start=245 -v end=275 'NR>=start && NR<=end {printf "%d\t%s\n", NR, $0}' "$file"

Repository: TanStack/tanstack.com

Length of output: 4065


🌐 Web query:

TypeScript handbook variance annotations in out generics instantiation-based comparisons structural variance trusted

💡 Result:

TypeScript utilizes a structural type system, meaning that type compatibility—and consequently variance—is naturally emergent from the members of a type rather than its name or explicit heritage [1][2][3]. Because of this, variance is automatically inferred by the compiler [1][4]. Variance annotations (in, out, and in out) are optional modifiers that allow developers to explicitly declare the variance of a generic type parameter [1][5]. Their primary purpose is not to change the structural behavior of a type, but to serve as a tool for enforcing intent, assisting with complex or recursive types, and potentially improving type-checking performance [1][4][6]. Key considerations for variance annotations include: 1. Structural vs. Instantiation-Based Comparisons: TypeScript performs two types of comparisons: structural and instantiation-based [1][4]. Variance annotations are only effective during instantiation-based comparisons [1][4]. Whether TypeScript chooses one over the other is internal, unspecified, and subject to change across compiler versions [1][4]. Therefore, you should only add a variance annotation if it matches the type's structural definition [1][4]. 2. Risk of Mismatch: Annotations do not override structural rules [1][4]. If an annotation is inconsistent with the actual structural behavior of the type, you will encounter unpredictable behavior or inconsistencies in type checking [1][4][5]. You should not use these annotations to "force" a specific variance [1]. 3. Inference Preference: Because TypeScript's automatic inference is highly accurate and handles the vast majority of cases, the official handbook strongly discourages using variance annotations in normal code [1][4]. They are intended for rare, specific scenarios where inference might fail, such as certain circular type definitions, or to provide explicit documentation of intent [1][4][6]. In summary, trust the compiler's automatic inference as the primary source of truth for variance. Use explicit annotations only when you have a specific, identified need and are certain the annotation matches the structural reality of the generic type [1][4].

Citations:


🌐 Web query:

TypeScript handbook generics variance annotations in out trusted simply trusted structural variance instantiation-based comparisons quotes

💡 Result:

In TypeScript, variance is a naturally emergent property of its structural type system [1][2]. TypeScript automatically infers the variance of generic type parameters based on how they are used within a type [1][3]. Because of this, explicit variance annotations—introduced in TypeScript 4.7 using in and out keywords—are almost never necessary and should generally be avoided in normal code [1][2][4]. Key points regarding variance annotations and structural behavior: 1. Automatic Inference: TypeScript calculates variance by observing how a type parameter is used (e.g., in input vs. output positions) [3][4]. You should only use annotations if you have identified a specific need, such as debugging or improving performance in complex, recursive type definitions [1][2][5][6]. 2. Structural vs. Instantiation-based Comparisons: Variance annotations are only consulted during instantiation-based comparisons [1][2][7]. They do not change the structural behavior of a type [1][2]. Because TypeScript's use of structural versus instantiation-based comparisons is not a specified behavior and can change between versions, you must only write variance annotations that match the actual structural variance of the type [1][2][7]. 3. Trust and Usage: You should not use variance annotations to "force" or change the variance of a type to something it is not structurally [1][7]. Doing so does not modify the type's behavior in structural contexts and can lead to surprising or incorrect type-checking results [1][2][7]. If you choose to use them, ensure the annotation is the same as the structurally measured variance [1][7]. 4. Annotation Meanings: - out T: Declares the type parameter as covariant (T only appears in output positions) [3][4][8]. - in T: Declares the type parameter as contravariant (T only appears in input positions) [3][4][8]. - in out T: Declares the type parameter as invariant [3][4][8]. In summary, variance annotations are an advanced tool meant to match—not override—the structural reality of your types [1][7]. They should be used sparingly and removed once the specific need (such as debugging) is resolved [1][2].

Citations:


Tone down the “variance annotation is simply trusted / always safe regardless of structure” claim for in out.
TypeScript variance annotations are consulted for instantiation-based comparisons and are intended to match the type’s structural variance; the current paragraph overstates safety (“simply trusted”, “sound regardless of structure”, “never permit an unsound one”).

Suggested rewording
- One subtlety worth being precise about: unlike a lone `in` or `out`, which the compiler does check against the type's structure, an `in out` annotation is simply trusted. That is sound to do regardless of the structure, because invariance is the strictest relation there is. Asserting it can only remove assignments the compiler would otherwise have allowed, never permit an unsound one. What the annotation buys is the shortcut. The compiler relates instantiations by their type arguments directly, with no variance probing and no structural fallback.
+ One subtlety worth being precise about: variance annotations are consulted for instantiation-based comparisons, and `in out` pins the parameter as invariant for those comparisons—letting the compiler skip variance probing/structural fallback when relating instantiations. Like any variance annotation, it’s meant to reflect the type’s structural variance; if it doesn’t, assignability can change (as your `TValue` break shows).
🧰 Tools
🪛 LanguageTool

[uncategorized] ~259-~259: “React” is a proper noun and needs to be capitalized.
Context: ...otating Table_Internal alone took the react adapter from 136k down to 66k, below wh...

(A_GOOGLE)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/blog/tanstack-table-v9-typescript-performance.md` around lines 257 - 261,
The paragraph overstates the safety of the `in out` variance claim; replace the
sentences that say "`in out` annotation is simply trusted" and "sound to do
regardless of the structure" with a more accurate explanation: state that `in
out` asserts invariance for the compiler's instantiation-based comparisons and
can bypass structural variance probing (i.e., the compiler will use the
annotated invariance rather than derive variance from the type's structure),
that this usually only restricts assignability but can also remove relations the
code relies on (as shown by `TValue` breaking the build), and therefore it
should be applied only where parameters are invariant in practice (keeping the
`Table_Internal` and `TValue` examples as-is to illustrate the benefit and
caveat).

---
title: TypeScript Performance in TanStack Table V9
published: 2026-06-13
excerpt: TanStack Table V9's types do a lot more than V8's did. Here's how we cut type instantiations by 66-85% across every package between the alpha.54 and beta.11 to keep the editor experience feeling nearly instant.

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.

"nearly"? why so defensive?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

In my own experience, there's usually still a noticeable few tenths of a second for the type to pop up. Plus there is still a lot of things going on that make this a slower typescript experience compared to v8. It's just way less of a time cost now and the tradeoffs are worth it.

So I didn't think "nearly" was defensive, just accurate


![TanStack Table V9 - TypeScript Performance](/blog-assets/tanstack-table-v9-typescript-performance/header.png)

TanStack Table V9 has a much more capable, though more complex, type system than V8. The types in Table may not be as complicated as a project like TanStack Router or Form, but it has still grown more complex in V9 than it ever had been in previous versions.

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.

is "type system" the right term here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I want a better term. What should it be?

@schiller-manuel schiller-manuel Jun 13, 2026

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.

"type-level API" maybe?


TanStack Table V9 has a much more capable, though more complex, type system than V8. The types in Table may not be as complicated as a project like TanStack Router or Form, but it has still grown more complex in V9 than it ever had been in previous versions.

If you had been using the Table V9 alphas, there's a chance that you could feel a bit of slowness in your editor. Good news, though! Between the alpha and the latest beta, we cut TypeScript's type-checking work by 66-85% across every package and example! The latest beta now type-checks faster than our alpha versions from last week by a wide margin, and the editor experience is back to feeling nearly instant.

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.

again "nearly"


If you had been using the Table V9 alphas, there's a chance that you could feel a bit of slowness in your editor. Good news, though! Between the alpha and the latest beta, we cut TypeScript's type-checking work by 66-85% across every package and example! The latest beta now type-checks faster than our alpha versions from last week by a wide margin, and the editor experience is back to feeling nearly instant.

This post covers where the cost came from, how we measured it, and the specific changes that fixed these issues. One of those changes is a still overlooked TypeScript feature that many library authors still seem to barely use, and it turned our to be one of our bigger optimizations, turning a trade-off into a win across the board.

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.

still still

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