Skip to content

Check TypeScript 7 compatibility in pnpm check - #255

Merged
SandroMaglione merged 1 commit into
mainfrom
typescript-7-compatibility
Nov 1, 2026
Merged

SandroMaglione merged 1 commit into
mainfrom
typescript-7-compatibility

Conversation

@SandroMaglione

Copy link
Copy Markdown
Member

Summary

  • Adds a TypeScript 7 compatibility lane to pnpm check, so both the check and latest-effect CI jobs run it. The root toolchain stays on TypeScript 6.0.3 because TypeDoc, tstyche, and the repository scripts need the JavaScript compiler API.
  • Fixes the one TypeScript 7 failure: tsconfig.tests.json reported TS2379 at packages/effect-machine/test/machine/Machine.test.ts:831 (Property 'code' is missing in type 'Payment' but required in type 'AuthorizedPayment').

Root cause: the test helper, not the library types

The library types are not involved. Under both compilers, planned.state.state is CompoundSnapshot<"payment", Payment, SnapshotWithPrefix<…>>, and it is assignable in both directions to the hand-written CompoundSnapshot<"payment", Payment, AtomicSnapshot<"payment.entering", EnteringPayment> | AtomicSnapshot<"payment.authorized", AuthorizedPayment>>. I checked this with a throwaway probe under 6.0.3 and 7.0.2.

The problem is the signature of assertCompoundStateSnapshot. Its actual parameter is CompoundSnapshot<Path, Value, Child> | CompoundSnapshot<"", undefined, CompoundSnapshot<Path, Value, Child>>. A non-root compound snapshot structurally matches both union members. Inference against the root-wrapper member reads candidates out of the child union (payment.entering/payment.authorized, EnteringPayment/AuthorizedPayment):

  • TypeScript 6 already infers Path = "payment" | "payment.entering" | "payment.authorized", which is wrong. Value happens to come out as Payment only because of candidate order.
  • TypeScript 7 orders union constituents differently, so Value becomes AuthorizedPayment and the call fails.

The fix wraps the root-wrapper member in NoInfer in all three helpers (assertStateSnapshot, assertCompoundStateSnapshot, assertParallelStateSnapshot). That way only the unwrapped snapshot and the explicit arguments drive inference. Consumers are not affected, so there is no library change.

TypeScript 7 lane

  • Why not a root alias: an alias like typescript7: npm:typescript@7.0.2 in the root does not work. pnpm resolves the tsc bin conflict by the higher version, so the root tsc silently became 7.0.2. Instead, a private workspace package, tooling/typescript-7, pins typescript@7.0.2 in its own node_modules. The root and package tsc stay 6.0.3 (checked).
  • pnpm typecheck:ts7: type-checks the four package projects (--noEmit) and tsconfig.tests.json with TypeScript 7. It runs in pnpm check right after pnpm typecheck, which emits the declarations the project references need. It takes about 11 s cold and about 3 s warm; the TypeScript 6 pnpm typecheck takes about 64 s cold. That speed is why it runs in pnpm check instead of a separate job.
  • pnpm test:consumer: now compiles the packed consumer with TypeScript 6 and then with the pinned TypeScript 7. TSGO_BIN still overrides the TypeScript 7 binary.
  • tstyche: it cannot target TypeScript 7. tstyche 7.2.1 and 7.2.5 restrict store targets to >=5.4 <7.0, so type assertions stay TypeScript 6 only. Only 8.0.0-beta.0 exists beyond that. This is documented in CONTRIBUTING.md.
  • latest-effect: its steps are unchanged. scripts/prepare-effect-compatibility.mjs only rewrites the root and packages/* manifests. Because the lane is part of pnpm check, that job now also runs the TypeScript 7 checks.

Changeset

  • Added or updated for a library or package-metadata change
  • Not required because this PR does not change a publishable package

This PR has an empty changeset (.changeset/typescript-7-compatibility-lane.md, created with changeset add --empty) and nothing more. scripts/check-changeset.mjs requires a changeset for any root package.json change, and this PR edits root scripts. An empty changeset releases nothing, the site's changelog parser ignores it, and changeset version just deletes it (checked locally).

Validation

  • pnpm check passed in 2:20: format, architecture, CI scripts, API and site docs, TypeScript 6 typecheck, TypeScript 7 typecheck, build, 92 files / 901 tests, 41 tstyche files / 281 tests (1334 assertions), consumer with TypeScript 6 and 7, and all four pack checks.
  • Without the helper fix, pnpm typecheck:ts7 exits 1 with the TS2379 above, so the lane catches it.
  • Type-performance measurement was not required: no src changes.
  • Runtime- and memory-performance measurement was not required: no runtime changes.

🤖 Generated with Claude Code

Pin the native TypeScript 7 compiler in a private tooling workspace package so
its tsc binary does not replace the TypeScript 6 toolchain, type-check every
package project and the test project with it, and compile the packed consumer
fixture with both compilers.

Fix the Machine test snapshot helpers so TypeScript 7 infers their type
parameters from the unwrapped snapshot rather than from the root-wrapper union
member.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@SandroMaglione
SandroMaglione force-pushed the typescript-7-compatibility branch from f41ab92 to afe650d Compare November 1, 2026 09:16
@SandroMaglione
SandroMaglione merged commit c829961 into main Nov 1, 2026
9 checks passed
@SandroMaglione
SandroMaglione deleted the typescript-7-compatibility branch November 1, 2026 09:20
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.

1 participant