fix(webgpu): narrow useThree/useFrame to WebGPURootState on the /webgpu entry - #3860
Merged
Conversation
…pu entry
The entry already re-exported WebGPURootState as RootState, but useThree and
useFrame arrived via `export * from '../core'` still typed against the base
RootState, whose `renderer` is the R3FRenderer union (WebGLRenderer included).
So anything WebGPU-only needed a cast on an entry point that has already
committed to WebGPU:
const renderer = useThree((s) => s.renderer)
renderer.compute(node)
// Property 'compute' does not exist on type 'R3FRenderer'
which is exactly the friction the split entry points exist to remove.
Both hooks are now re-declared against WebGPURootState. Explicit exports shadow
the star re-export in both ESM and TS, so this is types-only: the exported
values are the core implementations untouched, no runtime cost and no second
code path to keep in sync. A test pins that identity so the two cannot drift.
The signatures are structurally incompatible (the selector parameter makes them
contravariant), so the re-type goes through `unknown`. It is sound --
WebGPURootState is the same object the base hook already returns, with
renderer/gl/internal narrowed to what this entry guarantees at runtime.
Guarded by compile-time assertions rather than runtime ones: tsconfig includes
packages/**, so `pnpm typecheck` compiles the assertion file on every run.
Confirmed it fails pre-fix with the three errors from the issue, and that the
narrowing survives declaration emit -- dist/webgpu/index.d.ts now carries
`declare const useThree: UseThreeWebGPU`, exported exactly once.
Fixes #3851
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #3851.
The entry already re-exported
WebGPURootStateasRootState, butuseThreeanduseFramearrived viaexport * from '../core'still typed against the baseRootState, whoserendereris theR3FRendererunion (WebGLRenderer included). So anything WebGPU-only needed a cast, on an entry point that has already committed to WebGPU:Approach
Both hooks are re-declared against
WebGPURootState. Explicit exports shadow the star re-export in both ESM and TypeScript, so this is types-only — the exported values are the core implementations, untouched. No runtime cost, and no second code path to keep in sync. There's a test pinninguseThree === useThreeCoreso those can't quietly drift apart.The two signatures are structurally incompatible (the selector parameter makes them contravariant), so the re-type has to go through
unknown. It's sound:WebGPURootStateis the same object the base hook already returns, withrenderer/gl/internalnarrowed to what this entry guarantees at runtime.Testing
The assertions that matter are compile-time. The repo's tsconfig includes
packages/**/*, sopnpm typecheckcompiles the new assertion file on every CI run — a types regression fails the build. ThetypeAssertions()function is never called; its body only has to compile.Confirmed it fails pre-fix with exactly the errors from the issue:
Coverage includes the
DepthAttachmentSyncpattern the issue calls out, which previously needed(state.renderer as unknown as { backend?: ... }).backend.Verified through the build
Since the issue is about the shipped
dist/webgpu/index.d.ts, I checked the narrowing survives declaration emit rather than only holding in source:and both appear in the value export list exactly once — the shadowing doesn't produce a duplicate or ambiguous export.
pnpm typecheck✅ (and fails pre-fix, as above)pnpm build+pnpm verify-bundles+pnpm verify-types✅pnpm test✅ 582 passed, 46 filespnpm eslint/pnpm format✅Scope note
Narrowed
useThreeanduseFrame, the two the issue names. Other core hooks still take baseRootState; happy to extend if you'd rather do the whole surface at once, but I kept this minimal for alpha 4.🤖 Generated with Claude Code