Skip to content

fix(next): accept interface-typed next config in withPrewire - #1

Merged
clroot merged 1 commit into
mainfrom
fix/next-config-type
Aug 6, 2026
Merged

fix(next): accept interface-typed next config in withPrewire#1
clroot merged 1 commit into
mainfrom
fix/next-config-type

Conversation

@clroot

@clroot clroot commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Problem

withPrewire<C extends Record<string, unknown>> rejects NextConfig:

error TS2345: Argument of type 'NextConfig' is not assignable to parameter of
type 'Record<string, unknown> | NextConfigFn<Record<string, unknown>>'.
  Type 'NextConfig' is not assignable to type 'Record<string, unknown>'.
    Index signature for type 'string' is missing in type 'NextConfig'.

NextConfig is an interface, and interfaces get no implicit index signature.
The README's own snippet (withPrewire({ reactStrictMode: true })) passes only
because an object literal is inferred as an anonymous type — the moment an app
does the normal thing:

const nextConfig: NextConfig = { ... }
export default withPrewire(nextConfig)

it fails to typecheck. Hit while adopting prewire in a real Next 16 monorepo.

Fix

C extends object. The constraint only exists to exclude primitives; the
structural Record requirement buys nothing and excludes every interface.

The regression test deliberately declares an interface-typed config — an object
literal passes under either constraint, so it would not have caught this.

Also

README gets a section on resolving #prewire when shared packages are consumed
as source (transpilePackages and friends), which the current docs do not
cover. Two findings from the migration:

  • #… subpath imports are package-local by spec, so a #prewire inside
    packages/kit/src cannot resolve to the importing app's root via
    package.json imports. The app's tsconfig paths is sufficient and
    Turbopack honours it for transpiled workspace sources — no bundler alias
    needed
    (verified on Next 16.1.4, next build, 3 apps).

  • tsconfigPaths: true emits prewire.paths.gen.json for the app tsconfig to
    extends, but TypeScript replaces compilerOptions.paths across
    extends instead of merging. Any app with its own @/* alias silently loses
    the generated mapping. Documented the tsconfigPaths: false + inline entry
    route.

    Worth considering separately: codegen already patches package.json
    imports in place when conditions is set; doing the same for a
    user-named tsconfig would close the gap, but tsconfig is frequently JSONC
    and a comment-preserving rewrite is not free. Docs felt like the honest
    fix for now.

Verification

bun run lint, bun run build, bun run test, prettier --check . — all
green. @prewire/next tests 4 → 5.

`NextConfig` is an interface, and interfaces get no implicit index
signature, so the `C extends Record<string, unknown>` constraint rejected
`withPrewire(nextConfig)` for every app that types its config — which is
every app the docs tell you to write. The constraint is only there to
stop primitives, so `C extends object` covers it without the structural
requirement. Regression test uses an interface-typed config, since an
object literal passes under either constraint.

Also documents resolving `#prewire` when shared packages are consumed as
source (Next transpilePackages and friends): `#…` specifiers are
package-local by spec, so the mapping must come from the app's tsconfig
paths, and the emitted prewire.paths.gen.json cannot be extends-merged
by an app that declares paths of its own — TypeScript replaces `paths`
across `extends` rather than merging.
@clroot
clroot merged commit 3fb8a52 into main Aug 6, 2026
1 check passed
@clroot
clroot deleted the fix/next-config-type branch August 6, 2026 17:34
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