Type error with i18next@23.7.2+
rktyt opened this issue · 26 comments
🐛 Bug Report
The following error occurs:
error TS2741: Property 'reportNamespaces' is missing in type 'import("/tmp/test/node_modules/i18next/index").i18n' but required in type 'import("/tmp/test/node_modules/i18next/index").i18n'.
No errors with i18next versions 27.6.0, 27.7.0, 27.7.1.
I’m got errors with i18next versions 27.7.2, 27.7.3.
To Reproduce
The following three files are required to reproduce.
index.tsx:
import { createInstance } from 'i18next'
import type { ReactNode } from 'react'
import { initReactI18next, I18nextProvider } from 'react-i18next'
const i18n = createInstance()
await i18n.use(initReactI18next).init({
lng: 'en',
fallbackLng: 'en',
ns: ['common'],
defaultNS: 'common',
debug: false,
interpolation: {
escapeValue: false,
},
resources: { en: { common: {} } },
})
export function I18nextTestStubProvider({ children }: { children: ReactNode }) {
return <I18nextProvider i18n={i18n}>{children}</I18nextProvider>
}
package.json:
{
"dependencies": {
"i18next": "23.7.3",
"react": "^18.2.0",
"react-i18next": "^13.4.0"
},
"devDependencies": {
"@types/react": "^18.2.37",
"typescript": "^5.2.2"
}
}
tsconfig.json:
{
"compilerOptions": {
"target": "ESNext",
"useDefineForClassFields": true,
"lib": ["DOM", "DOM.Iterable", "ESNext"],
"allowJs": false,
"skipLibCheck": true,
"esModuleInterop": false,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"strictNullChecks": true,
"removeComments": true,
"noEmit": true,
"jsx": "react-jsx",
"baseUrl": "."
}
}
After creation, check with npm install && npx tsc --noEmit
Expected behavior
No type errors.
Your Environment
- runtime version: node v18
- i18next version: 23.7.3
- os: Linux
Additionnal context
Probably the same as #1379.
However, the cause seems to be different because multiple versions of i18next are not installed.
Can you provide a reproducible example please?
Same here 🙋♂️
I also created a minimum reproduction:
https://stackblitz.com/edit/vitejs-vite-vpz18f?file=src%2Fmain.ts
seems this is because of the moduleResolution set to bundler... if you change it to NodeNext, it works
@adrai remix-utils requires "moduleResolution": "Bundler"
in tsconfig to resolve ESM/CJS conflicts.
If you have an idea on how to "fix" this, feel free to provide a PR.
@pedrodurek @JounQin @perrin4869 @me4502 @ubuntudroid @nicegamer7 can you help?
cc: @sergiodxa (the author of remix-i18next)
v13.4.1 should be better
i18next v23.7.6 + react-i18next v13.4.1
Alternatively, try to cast it:
import { createInstance, i18n as I18N } from "i18next";
import { useTranslation } from "react-i18next";
const i18n = createInstance();
useTranslation("test", { i18n: i18n as I18N });
Still a type issue in 23.7.6 and 13.4.1 when setting the <I18NextProvider i18n={instance}/>
Is there something I can add to the react-i18next.d.ts
file?
This is what I have now.
import type resources from '~/languages/en';
import 'react-i18next';
declare module 'i18next' {
interface CustomTypeOptions {
resources: typeof resources;
}
}
Everything still works as expected when I @ts-ignore the type error.
// @ts-ignore
<I18nextProvider i18n={instance}>
Did you try to cast it? Like here: #1693 (comment)
Yes, but it doesn’t resolve the type error with the Provider i18n prop. The example above is used with the useTranslation hook, but that isn’t an issue in remix. Just the provider.
Did you try this?
import { i18n as I18N } from "i18next";
// ...
<I18nextProvider i18n={instance as I18N}>
This works for me:
import { createInstance, i18n as I18N } from 'i18next'
import type { ReactNode } from 'react'
import { initReactI18next, I18nextProvider } from 'react-i18next'
const i18n = createInstance()
i18n.use(initReactI18next).init({
lng: 'en',
fallbackLng: 'en',
ns: ['common'],
defaultNS: 'common',
debug: false,
interpolation: {
escapeValue: false,
},
resources: { en: { common: {} } },
})
export function I18nextTestStubProvider({ children }: { children: ReactNode }) {
return <I18nextProvider i18n={i18n as I18N}>{children}</I18nextProvider>
}
In remix-i18next, on the entry.server file, you do this:
https://github.com/sergiodxa/remix-i18next#server-side-configuration
let instance = createInstance();
let lng = await i18next.getLocale(request);
let ns = i18next.getRouteNamespaces(remixContext);
await instance
.use(initReactI18next) // Tell our instance to use react-i18next
.use(Backend) // Setup our backend
.init({
...i18n, // spread the configuration
lng, // The locale we detected above
ns, // The namespaces the routes about to render wants to use
backend: { loadPath: resolve("./public/locales/{{lng}}/{{ns}}.json") },
});
return new Promise((resolve, reject) => {
let didError = false;
let { pipe, abort } = renderToPipeableStream(
<I18nextProvider i18n={instance}>
<RemixServer context={remixContext} url={request.url} />
</I18nextProvider>,
I tried i18n={instance as I18N}
but the error persists.
You're still not casting the instance in your code.
Instead of:
<I18nextProvider i18n={instance}> <RemixServer context={remixContext} url={request.url} /> </I18nextProvider>,
Do:
<I18nextProvider i18n={instance as I18N}> <RemixServer context={remixContext} url={request.url} /> </I18nextProvider>,
That’s not my code, I pasted the code example from the docs I linked to. Sorry for the confusion I was trying to add more context.
In my code, I cast it as I mentioned:
I tried
i18n={instance as I18N}
but the error persists.
So can you please provide your complete reproducible Code repository?
Or keep the ts-ignore 🤷♂️
Can’t do the first, unfortunately. I’ll keep the ts-ignore but other remix-i18next users may run into the same issue so I’ll have Sergio make a note about it.
Got it working with the casting.
this module augmentation is the problem:
Line 41 in fdab4e8
the property should be optional. however, changing it to optional might be a breaking change?
v14.0.0 has just been released that should make it optional
v14.0.0 has just been released that should make it optional
thanks for the super quick release!