Closed ffxsam closed 3 years ago
I'm not a maintainer but I've just looked at the index.d.ts
file and it appears that the expected return value of the t
/$t
methods is VueI18n.TranslateResult
which in turn is defined as type TranslateResult = string | LocaleMessages;
LocaleMessages
is defined as interface LocaleMessages { [key: string]: LocaleMessageObject; }
which is an indexable type, which strictly could be empty.
This means that your consuming code will yell at you if a string is expected - e.g. values in objects, calls to Error()
etc as a call to t
/$t
may not be a string.
Digging a bit deeper into the t
method call chain (in src/index.ts
) it appears that it actually won't always return a string. This exception to a string type return occurs where this._translate
is called inside _t
into a prepared into the constant ret
which can return null, so may that's why the return type is defined as any. Maybe this is an area for exploration and improvement to be stricter on the types defined so that there is always a string (or at least an empty string) to trickle back up the call chain to ensure the return value for t
is always a string. In particular, there is a line in _translate
which checks !isNull(res)
. I wonder if this can be refactored to a !== ''
check instead.
The thing with typescript is that it enforces best practices with return values and it's best not to mix them wherever possible. For example if a function returns a string for its happy path then the null-ish value ought to be an empty string; not null or false or anything else.
Anyway, just my 2 cents. I don't really have a solution to offer but I hope this information is useful if one of the maintainers are able to investigate this.
Is this still on the radar? :)
Still thousands ' as string'-s in thousands of projects... :)
Quick note, using .toString()
also works, and is probably more correct than type-casting.
In the end i have used patch-package
.
yarn add patch package
Then changed type TranslateResult = string | LocaleMessages;
to type TranslateResult = string;
Then
npx patch-package vue-i18n
And to automatically patch typings for all team members added to postinstall in package.json:
"scripts": {
"postinstall": "patch-package"
}
It'll not work in ALL cases, but it's working for me.
You could also use $tc
instead of $t
whenever you just need the translation without further ado as this function returns a string always.
use this interfaces.d.ts filein root of your projecz
// Extend Vue with our custom types import Vue from 'vue' declare module 'vue/types/vue' { interface Vue { // == translations $t: (name: string, attrs?: any) => string $tc: (name: string, count: number, attrs?: any) => string } }
// Needs to be here export {}
// vue-i18n.d.ts
import VueI18n, {
Path, Values, Locale,
} from 'vue-i18n/types'
/**
* Overloads VueI18n interface to avoid needing to cast return value to string.
* @see https://github.com/kazupon/vue-i18n/issues/410
*/
declare module 'vue-i18n/types' {
export default class VueI18n {
t(key: Path, locale: Locale, values?: Values): string;
t(key: Path, values?: Values): string;
}
}
declare module 'vue/types/vue' {
interface Vue {
$t: typeof VueI18n.prototype.t;
}
interface VueConstructor<V extends Vue = Vue> {
i18n: typeof VueI18n.prototype;
}
}
export {}
Did the trick for me.
Just importing TranslateResult type from i18 library.
@piktur Thank its working but now I had a problem with This expression is not constructable.
or No overload matches this call....
after import 'vue-i18n' in .ts.
I fixed this with changed last Your line
// vue-i18n.d.ts
import VueI18n, {
Path, Values, Locale,
} from 'vue-i18n/types'
/**
* Overloads VueI18n interface to avoid needing to cast return value to string.
* @see https://github.com/kazupon/vue-i18n/issues/410
*/
declare module 'vue-i18n/types' {
export default class VueI18n {
t(key: Path, locale: Locale, values?: Values): string;
t(key: Path, values?: Values): string;
}
}
declare module 'vue/types/vue' {
interface Vue {
$t: typeof VueI18n.prototype.t;
}
interface VueConstructor<V extends Vue = Vue> {
i18n: typeof VueI18n.prototype;
}
}
- export {}
+ export default VueI18n
Thanks @Zummek
we supported in Vue I18n v9. translation function return string. please try it!
Except that, if I understand correctly, Vue i18n v9 is only compatible with Vue.js 3 and more. So this improvement will have to wait a loooong time before being mass tested.
@piktur Thank its working but now I had a problem with
This expression is not constructable.
orNo overload matches this call....
after import 'vue-i18n' in .ts. I fixed this with changed last Your line// vue-i18n.d.ts import VueI18n, { Path, Values, Locale, } from 'vue-i18n/types' /** * Overloads VueI18n interface to avoid needing to cast return value to string. * @see https://github.com/kazupon/vue-i18n/issues/410 */ declare module 'vue-i18n/types' { export default class VueI18n { t(key: Path, locale: Locale, values?: Values): string; t(key: Path, values?: Values): string; } } declare module 'vue/types/vue' { interface Vue { $t: typeof VueI18n.prototype.t; } interface VueConstructor<V extends Vue = Vue> { i18n: typeof VueI18n.prototype; } } - export {} + export default VueI18n
Any idea why this might be completely ignored by my IDE Webstorm? That is to say, the error is still shown. I have other types of my own that do work.
Refer to this shim, create vue-i18n.d.ts
to export VueI18n as variable:
import VueI18n from 'vue-i18n/types'
/**
* Replace default export of vue-i18n
* @see https://github.com/Microsoft/TypeScript/issues/14080#issuecomment-1050833256
*/
export { VueI18n }
then import it in shims-vue-i18n.d.ts
import {
Path, Values, Locale
} from 'vue-i18n/types'
import { VueI18n } from './vue-i18n'
/**
* Overloads VueI18n interface to avoid needing to cast return value to string.
* @see https://github.com/kazupon/vue-i18n/issues/410
*/
declare module './vue-i18n' {
interface VueI18n {
t(key: Path, locale: Locale, values?: Values): string
t(key: Path, values?: Values): string
}
}
declare module 'vue/types/vue' {
interface Vue {
$t: typeof VueI18n.prototype.t
}
interface VueConstructor<V extends Vue = Vue> {
i18n: typeof VueI18n.prototype
}
}
export default VueI18n
This is part question, part feature request.
In my code, I constantly have to typecast:
If I don't, I get this error: Type 'TranslateResult' is not assignable to type 'string'
Is there some workaround for this? Maybe something we can declare once, so it always returns string?