#How can I have the return type inferred based on the key and use const?

47 messages · Page 1 of 1 (latest)

fleet kraken
#
import { resolve as resolvePath } from 'path';
import { readFile } from 'fs/promises';

// import type baseLanguage from '@app/locales/en-AU.json';
// This const is the data from the .json file
const baseLanguage = {
    botName: 'Jive',
    number: 123
};

const defaultLanguage = 'en-AU' as const;

const knownLanguages = [
    'en-AU',
    'en-US'
] as const;

type I18n = Record<typeof knownLanguages[number], Partial<typeof baseLanguage>>;

const i18n = {} as I18n;

const getLanguage = async (language: typeof knownLanguages[number] = defaultLanguage) => {
    try {
        i18n[language] ?? await readFile(resolvePath(__dirname, 'src/locales/', `${language}.json`), 'utf-8').then(file => {
            const loadedLanguage = JSON.parse(file) as Partial<typeof baseLanguage>;
            i18n[language] = loadedLanguage;
            return loadedLanguage;
        });
    } catch { }

    return {} as Partial<typeof baseLanguage>;
};

export const t = async (key: keyof I18n[keyof typeof i18n], language: typeof knownLanguages[number] = defaultLanguage) => {
    // Check if the language is loaded
    // If not then load it
    const loadedLanguage = await getLanguage(language);

    // Check if the key is in the language
    const value = loadedLanguage[key];
    if (value === undefined) throw new Error(`Missing key "${key}" for "${language}".`);
    return value;
};

void t('botName').then(value => {
    console.log(typeof value); // string | number <-- this should be "Jive" as a const
});
obsidian fox
#

I think your problem is you're returning a Partial<typeof baseLanguage> which might not have the botname key

fleet kraken
#

Is there another way around this then? I know the base language will contain all keys but others may be missing some. 🤔

obsidian fox
#

can you just remove the Partial for the base language type then after you parse it

#

if it can fail with an error, you don't really have a guarantee it really is populated though

restive swallow
#

Why should value be of type Jive?

obsidian fox
#

so im not sure you really can guarantee it is jive

restive swallow
#

You can't, because if it wasn't the base language, the type will be different.

#

Unless it's Jive in every single language, then you are not translating anything.

fleet kraken
#

Yes I get that.. the reason it's Jive is that it shows the base language to the developer while in the IDE, this is the same as some other i18n setups work. Figured I'd ask in here about this before I go and look at how they're all doing it.

restive swallow
#

If other i18n setups work like that, then that's just bad design.

#

That's simply lying to TS about the type.

fleet kraken
#

look if you dont want to help... dont?

#

no need to come in here like that bud..

restive swallow
#

I'm trying to explain to you why that's a bad idea, but sure.

fleet kraken
#

i didnt ask about that...

fleet kraken
obsidian fox
#

I dont have a full understanding of what the intent is

fleet kraken
#

key in value as const out.

obsidian fox
#

what is the purpose of base language

fleet kraken
#

to hold all the keys that should exist in the typing for t(key)

obsidian fox
#

I see

fleet kraken
#

The idea is I can do t('botName') and in the IDE we'll get "Jive" as a const and at runtime the value of whatever language.

#

I may be completely going at this in the wrong direction.

obsidian fox
#

so first of all your const baseLanguage = {} is not immutable, so it needs to be const to do anything

#

but that doesnt solve the problem, just pointing it out

#

or, satisfies

fleet kraken
#

For what its worth that was a json import before which I believe was making it const.

#

oh....

#

you know what that fixes it.

#

added as const to the baseLanguage and we're good.

#

well not good but closer..

obsidian fox
#

there's still the issue of if it fails, you're asserting a type that's incorrect

fleet kraken
#

still have the issue of it returning a combined union of all possible types.

dull sinewBOT
#
Burrito#6903

Preview:```ts
// import type baseLanguage from '@app/locales/en-AU.json';
// This const is the data from the .json file
const baseLanguage = {
botName: 'Jive',
number: 123
} as const;

const defaultLanguage = 'en-AU' as const;

const knownLanguages = [
'en-AU',
...```

obsidian fox
#

do you have to return an empty object? can you just throw if it fails? you shouldn't be importing files that don't exist, right?

#

so you can do an assertion function

restive swallow
#

Here's exactly what you want, but yeah, imo still a bad idea.

fleet kraken
#

What was the change needed?

obsidian fox
#

added const

restive swallow
#

as const your baseLanguage, changed the t signature.

fleet kraken
#

!resolved

obsidian fox
#

getLanguage does not use language

#

but I will stop if you're over it 🙂

restive swallow
#

I just removed the getLanguage implementation because it's irrelevant to the question.

obsidian fox
#

I see