I'm using the i18next library in a TS app (not React) and getting an unexpected Typescript error.
I'm getting a type mismatch when calling the getFixedT
function. I'm passing strings for both lng
and ns
, yet getting errors expecting null
.
I have the following type definition in i18next.d.ts
:
// i18next.d.ts
import "i18next";
import definitions from "../i18n/locales/definitions/en.json";
import errors from "../i18n/locales/errors/en.json";
const NAMESPACES = {
definitions: "definitions",
errors: "errors",
};
declare module "i18next" {
interface CustomTypeOptions {
ns: typeof NAMESPACES.errors | typeof NAMESPACES.definitions;
returnNull: false;
resources: {
en: {
definitions: typeof definitions;
errors: typeof errors;
}
};
}
}
My resource files are defined by namespace, like so:
// src/i18n/locales/resources.ts
import definitions_de from './definitions/de.json';
import definitions_en from './definitions/en.json';
import errors_de from './errors/de.json';
import errors_en from './errors/en.json';
export const resources = {
de: {
definitions: definitions_de,
errors: errors_de,
},
en: {
definitions: definitions_en,
errors: errors_en,
},
// <etc>
}
I init i18nInstance
and export it here:
// src/i18n/index.ts
import i18n, { createInstance, InitOptions } from 'i18next';
import { fallbackLng, NAMESPACES } from '../constants';
import { resources } from './locales/resources';
const i18nInstance = createInstance();
void i18nInstance
.init({
fallbackLng, // use en if detected language is not available
defaultNS: NAMESPACES.errors,
resources,
load: 'currentOnly', // only load the detected language
ns: [NAMESPACES.definitions, NAMESPACES.errors], // namespaces
saveMissing: true, // send not translated keys to endpoint
returnNull: false,
} as InitOptions);
export default i18nInstance;
In my code, I'm calling the getFixedT
function likes so:
const locale = "en";
const t = i18nInstance.getFixedT(locale, NAMESPACES.errors)
t("path.to.definition", { variable: "value" });
I get the following ts error:
No overload matches this call.
Overload 1 of 2, '(lng: null, ns: Namespace<"en"> | null, keyPrefix?: KeyPrefix<Namespace<"en">>): TFunction<Namespace<"en">, KeyPrefix<Namespace<"en">>, Namespace<...>>', gave the following error.
Argument of type 'Locale' is not assignable to parameter of type 'null'.
Type '"de"' is not assignable to type 'null'.
Overload 2 of 2, '(lng: string | readonly string[], ns?: Namespace<"en"> | null | undefined, keyPrefix?: undefined): TFunction<Namespace<"en">, undefined, Namespace<"en">>', gave the following error.
Argument of type 'string' is not assignable to parameter of type 'Namespace<"en"> | null | undefined'.ts(2769)
I was able to reproduce your issue and found 2 problems, and a solution 🙃
Namespace<"en"> | null | undefined
First, according to https://www.i18next.com/overview/typescript, resources
should contain just the namespaces (without wrapping them in language code). For example, you can use your resources.ts
, and do:
declare module "i18next" {
interface CustomTypeOptions {
// I removed `ns`, as it is not supported in i18n
returnNull: false;
resources: typeof resources['en']; // equivalent of typeof { definitions: {...}, errors: {...} }
}
}
After above change, I got this error:
Argument of type 'string' is not assignable to parameter of type '`Namespace<"definitions" | "errors"> | null | undefined'.ts(2769)
Remaining problem is that TypeScript can not infer the type of your namespace keys (each key was treated as a plain string, instead of a literal value). We can fix that with as const
:
// As const
export const NAMESPACES = {
definitions: "definitions",
errors: "errors",
} as const;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With