Skip to content

[Docs]: TypeScript integration: Add some examples for utility types #1704

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
sipos-david opened this issue Feb 7, 2025 · 2 comments
Closed

Comments

@sipos-david
Copy link

I created some utility types which I think would be nice to have on the TypeScript integration page. I don't think the package itself should export all these, because the types depends on configuration of the user. Locale segment in pages can be handled multiple ways and the type can be narrowed from string if need be and Next 15+ uses promises.

These are for passing around translations (useful for lists with dynamic translations, you don't have to load translations in every child):

// import type needs TypeScript 3.8+
import type { NamespaceKeys, NestedKeyOf, useTranslations } from "next-intl";
import type { getTranslations } from "next-intl/server";

/**
 * Type for passing around NextIntl getTranslation or useTranslations functions
 */
export type getTranslationsType<
  T extends NamespaceKeys<IntlMessages, NestedKeyOf<IntlMessages>>,
> =
  | Awaited<ReturnType<typeof getTranslations<T>>>
  | ReturnType<typeof useTranslations<T>>;

/**
 * Type for passing around keys for given getTranslation or useTranslations functions
 */
export type getTranslationsKeys<
  T extends NamespaceKeys<IntlMessages, NestedKeyOf<IntlMessages>>,
> = Parameters<getTranslationsType<T>>[0];

Usage:

// messages/en.json
{
  "hello": {
     "world": "Hello world!" 
   }
}

// getTranslationsType

function MyList() {
   const t = getTranslations("hello"); // or useTranslations("hello") in client components.
   return (
     <ul>
       {items.map(item => (<MyListItem t={t}>))}
     </ul>
   );
}

function MyListItem(props: {
  t:  getTranslationsType<"hello">
}) {
   return (
     <li>
        {props.t("world")} // Autocomplete works here for keys.
     </li>
   );
}

//  getTranslationsKeys

function MyComponent(props: {
  key:  getTranslationsKeys<"hello">
}) {
   const t = getTranslations("hello"); // or useTranslations("hello") in client components.
   return (
     <h1>
        {t(props.key)}
     </h1>
   );
}

For Next.js 15+ a type for page and layout props which forces the ussage of promises. I don't know if these type interferes with Dynamic IO, I assume the Next compiler uses the actual code, not the TypeScript types given by the user.

import type { PropsWithChildren } from "react";

/**
 * Type for page props that already contains "locale" segment.
 */
export type LocalePageProps<T = unknown> = {
  readonly params: Promise<{
    /**
     * Segment that determines the locale.
     */
    readonly locale: string;
  }>;
} & Readonly<T>;

/**
 * Type for layout props that already contains "locale" segment and "children".
 */
export type LocaleLayoutProps<T = unknown> = PropsWithChildren<
  LocalePageProps<T>
>;

Usage:

// page.tsx
export default async function Page(
  props: LocalePageProps
) {
  setRequestLocale((await props.params).locale);
  
  // Rest of the page
}

// [id]/page.tsx
export default async function Page(
  props: LocalePageProps<{
    // searchParams, or other Next page props can be defined here too.
    params: {
      id: string // string | readonly string[] | undefined  would be a safer option
    }
  }>
) {
  const params = await props.params

  setRequestLocale(params.locale);

  const data = getData(params.id);
    
  // Rest of the page
}

// layout.tsx
export default async function Layout(props: LocaleLayoutProps) {
  // Enable static rendering
  setRequestLocale((await props.params).locale);

  return props.children;
}
@amannn
Copy link
Owner

amannn commented Feb 7, 2025

Thank you for carefully writing down your thoughts!

The question about typing the return type of useTranslations comes up from time to time, but I'm hesitant to recommend passing t to a function since it might not work with future optimizations like #1. My recommendation is to never pass t to a function and instead use it as returned from useTranslations/getTranslations.

Regarding LocalePageProps, I think once rootParams lands, you can avoid reading locale from params at all and just use useLocale()/getLocale() where relevant. For the time being, I think this type can be useful though, I'm using it myself in projects! 🙂

@sipos-david
Copy link
Author

Thank you! I somehow missed the discussions about return type of useTranslations, sorry.

I see your point, it's better for users to make a conscious choice about passing around translations rather then immediately available from the docs.

If someone needs this, it's available in this issue.

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

No branches or pull requests

2 participants