Next.js Discord

Discord Forum

Layout params union types casted to string..

Unanswered
Couch's Kingbird posted this in #help-forum
Open in Discord
Couch's KingbirdOP
Build is failing because next js cast union type to string.

Types of property 'params' are incompatible.
Type 'Promise<{ locale: string; }>' is not assignable to type 'Promise<{ locale: "fr" | "nl" | "en"; }>'.


export const dynamicParams = false;

export async function generateStaticParams(): Promise<{ locale: Locale }[]> {
  return i18n.locales.map((locale) => ({ locale }));
}

export default async function LocaleLayout({children, params}: {
  children: React.ReactNode;
  params: Promise<{ locale: Locale }>;
}) {
  const { locale } = await params;
  const { tr, dict } = await getTranslations(locale);

8 Replies

Couch's KingbirdOP
Thanks for checking. Here's a minimal reproduction:
https://github.com/Tiktak-dev/nextjs-params-error

Created with create-next-app defaults, four files added. npm run build
fails type checking with two errors.

Two things probably explain why it worked for you.

1. It only reproduces with strictFunctionTypes on, which strict: true
implies — and strict: true is the create-next-app default. It makes
parameter types checked contravariantly, so the narrowed Locale union is
rejected. With it off, bivariance accepts the narrowing. On my real project:
7 errors with strict, 4 without, and the 5 that disappear are all layouts.

2. A page declaring the exact same narrowed params produces no error at
all — only layouts do. The generated contracts differ:

default: React.ComponentType<{ params: Promise<ParamMap[Route]> } & any> // AppPageConfig
default: React.ComponentType<LayoutProps<Route>> // LayoutConfig

The page contract is intersected with any, which absorbs any mismatch, so
page params are effectively never validated. Layouts are validated strictly.

That also explains the asymmetry noted in #82820 ("for layouts and routes,
but not for the pages"), which was closed and locked without a cause being
identified.

So if you tested a page, or a project without strict mode, it would look
fine. Could you try the repo with the layout?
@Couch's Kingbird Thanks for checking. Here's a minimal reproduction: https://github.com/Tiktak-dev/nextjs-params-error Created with `create-next-app` defaults, four files added. `npm run build` fails type checking with two errors. Two things probably explain why it worked for you. 1. It only reproduces with `strictFunctionTypes` on, which `strict: true` implies — and `strict: true` is the create-next-app default. It makes parameter types checked contravariantly, so the narrowed `Locale` union is rejected. With it off, bivariance accepts the narrowing. On my real project: 7 errors with strict, 4 without, and the 5 that disappear are all layouts. 2. A *page* declaring the exact same narrowed `params` produces no error at all — only layouts do. The generated contracts differ: default: React.ComponentType<{ params: Promise<ParamMap[Route]> } & any> // AppPageConfig default: React.ComponentType<LayoutProps<Route>> // LayoutConfig The page contract is intersected with `any`, which absorbs any mismatch, so page params are effectively never validated. Layouts are validated strictly. That also explains the asymmetry noted in #82820 ("for layouts and routes, but not for the pages"), which was closed and locked without a cause being identified. So if you tested a page, or a project without strict mode, it would look fine. Could you try the repo with the layout?
alr, I fixed your issue. The commit is currently pending, as I am not allowed to push to the repo. I'll delete the repo in 2 days from my PC if you dont answer.

in general: nextjs types params always as string instead of a literal. So you need to assert it first and only when you do that, you got typesafety on your side
Couch's KingbirdOP
Whenever you get a chance — feel free to open a PR from your fork, or just paste the diff here. Also, is it a patch on Next itself, or a workaround in the repro? Asking because the issue is about the validator rejecting the union, not about the assertion workaround.
Couch's KingbirdOP
Thanks for taking the time. That's the workaround I'm already using in production though — the issue isn't about making it compile. It's that the generated ParamMap derives from the folder name and ignores what generateStaticParams returns, so the validator rejects a type the framework already guarantees under dynamicParams = false.

Vercel's triage read it the same way and opened a failing regression test for it: https://github.com/vercel-labs/next.js/pull/122

Closing this one, but thanks anyway.
@Couch's Kingbird solved?