#Function with optional parameters that depend of the first one

42 messages · Page 1 of 1 (latest)

tidal charm
#

Hi,

I want to translate my CV and some translations need vars, so I try this:

// src/i18n/ui.ts
import { i18n } from "@/i18n/config";

export const languages = {
  en: "English",
  es: "Español",
};

export const defaultLang = i18n.defaultLocale;
export const showDefaultLang = false;

export const ui = {
  es: {
    title: "Portafolio de",
    "sections.about": "Sobre mí",
    "sections.experience": "Experiencia laboral",
    "sections.education": "Educación",
    "sections.projects": "Proyectos",
    "sections.skills": "Habilidades",
    "common.present": "Actual",
    "common.seeLink": ({ link }) => `Ver ${link}`,
    "see.email": ({ name, email }) => `Enviar un correo electrónico a ${name} al correo ${email}`,
    "see.phone": ({ name, phone }) => `Llamar por teléfono a ${name} al número ${phone}`,
    "see.network": ({ name, network }) => `Visitar el perfil de ${name} en ${network}`,
  },
  en: {
    title: "Portfolio of",
    "sections.about": "About me",
    "sections.experience": "Work Experience",
    "sections.education": "Education",
    "sections.projects": "Projects",
    "sections.skills": "Skills",
    "common.present": "Present",
    "common.seeLink": ({ link }) => `See ${link}`,
    "see.email": ({ name, email }) => `Send an email to ${name} at ${email}`,
    "see.phone": ({ name, phone }) => `Call ${name} at ${phone}`,
    "see.network": ({ name, network }) => `Visit ${name}'s profile on ${network}`,
  },
} as const;
// src/i18n/utils.ts

import { ui, defaultLang, showDefaultLang } from './ui';

export function getLangFromUrl(url: URL) {
  const [, lang] = url.pathname.split('/');
  if (lang in ui) return lang as keyof typeof ui;
  return defaultLang;
}

type KeysOfValue<T, V extends T[keyof T]> = 
  { [K in keyof T]-?: T[K] extends V ? K : never }[keyof T];

type PickOfValue<T, V extends T[keyof T]> = Pick<T, KeysOfValue<T, V>>;

type functionsKeys = PickOfValue<typeof ui[typeof defaultLang], (...args: any) => any>;

export function useTranslations(lang: keyof typeof ui) {
  return function t<T extends keyof functionsKeys | keyof stringKeys>(key: T, vars: (T extends keyof functionsKeys ? Parameters<typeof ui[typeof defaultLang][T]>[0] : never)) {
    const translation = ui[lang][key] || ui[defaultLang][key];
    return (typeof translation === 'function' ? translation(vars) : translation) satisfies string;
  }
}

export function useTranslatedPath(lang: keyof typeof ui) {
  return function translatePath(path: string, l: string = lang) {
    return !showDefaultLang && l === defaultLang ? path : `/${l}${path}`
  }
}
---
import { getLangFromUrl, useTranslations } from '@/i18n/utils';

const { work } = Astro.props

const lang = getLangFromUrl(Astro.url);
const t = useTranslations(lang);
---

<ul>
  {
    work.map(
      ({ name, endDate, url }) => {
        const endYear =
          endDate != null ? new Date(endDate).getFullYear() : t('common.present')

        return (
          <a href={url} title={t('common.seeLink', { link: name })} target="_blank">
            {name}
          </a>
        )
      }
    )
  }
</ul>

But there is some errors:

  • useTranslations: error type in vars
  • t: vars is not typed as an optional when the key is not a function
  • ui: the functions object argument keys should be type automatically as a string

Does anyone knows how to fix it?

https://cdn.discordapp.com/attachments/1214284555020935279/1214284556157325332/image.png?ex=65f88d9e&is=65e6189e&hm=c87352ec6019a42da356977fcc67203401f1a53671289465df2a66fb5864bf99&

https://cdn.discordapp.com/attachments/1214284555020935279/1214284555792416768/image.png?ex=65f88d9e&is=65e6189e&hm=90f272ea3b49b02e78b286e19cc346d9acb8a40414b9aab53190a7b34bf9b17c&

https://cdn.discordapp.com/attachments/1214284555020935279/1214284556501520434/image.png?ex=65f88d9e&is=65e6189e&hm=2a8a1e0dd6189a00a0d9d64d481b675bc2724b291f00e622db0948e6a251c790&

#

Function with optional parameters that depend of the first one

gentle tinsel
cerulean ether
#

@tidal charm just make the parameter key properties optional

tidal charm
# gentle tinsel I think it could help if you could provide simplified reproductions of the probl...

you are right, here is the demo:

https://www.typescriptlang.org/play?#code/KYDwDg9gTgLgBAYwgOwM7wJYEYAcy4C8cA3gFBxwAmwAZgIYCuANjADIQJ1PABccARMFT8ANOThMOXIXwDag4SIHBk-ALpiAvnDqpEKdAG5SJ0JFj608JnWQBzBnTtDCJcSr78AovaYZUABai7qieXqhgdACPEEzBmsakZtDwSFZUtIwsrLZ2rth4AHTU9MxsUtzGyRZp6HCBEADuACKZZTn2rvRMqMCJAPT9cAAKUBAARtwAtnBYfACScI228DAQcDAAnmDAOkxMI3RQdFPAMMBQehA0GwG7NAzICDAYKDp6xADWwAw86FAYezaRoYGABCAMawQXq3XaMNZIKZgbgwOgvN7XWFwb6bVBJcApSx1BgYVxkCgyNwUCgvGDcTzDFJ0GixV4ZYLUgS9Z6vNCFOjjCEwfieADKEyguymAFuOdT+Nz0XyzBcMCoEMARQIvOBVeqMHQJALoFw5RQFcAeQZCsBKAxOEqtd47ZwEBgAM+qMSci1WvlgMYAK0tMGEDLGmxD0LNXJDvNQhVQnww+zDAgAEgKUxhKHRqIpxObEVMUIUA0IVMLPABBZ6OOLe+XF0u9YCsQGfLUACmIEg7cE0AEpCAA+OAAAwAahc4AASYh+ZCfTTjxvm1s2qZ0FPd3vIE7AJTALcpgfDghj8c+ABuBqgcEe+igkvWwG4zygnowSB0c+I+9ObQuCfF8-2PbcmBXNdY2AMtwWQTU+B7OAAMPOAwHg3Yh1HCdWBsLd73MDY3wAS5ZZB1kNedUKAg5kAAL9OMY-wwlBgCgwsYMKBCYEaaBOyQvcDyUHi+KgT4zxwqd-FBI44DfdCLhoU9qD-Gj5PwaizjE5dV3ETRGw8KlqVpekBEZWAWT8dZrhjX0lQTY1ISdatBUhOBTjsxV4xtXUAXVRCBAAdX4uAdR2fynk1aD7J8217TRXknS8F1EpQLy42tAMIGDZ4034UYcpDAsfW860kxTHonVFZNUzs5tkDLSVemQKtzOayt6ogKYS0a1t2yXXc+yXSSLwnUVgF2edF10mKN3AndBJQ4T5JPA5sLG8cJuQSgdHwBaDjWNSDyA+B5wOjjSsmuC2KG1ClFYhDRsvABhLgDmok6dDO4hHvYvSrtg0T+LulbgfE56J0nGSfpogByPRsuU7g4DeLTeP4y6By0d4iRgRJqngB4niVOBnDYXIADEximABVKAmC7BgGb4WmACVWGHck8bgWQlBsew1FcZmmDLNEAlQxNkVBLs4f6OHB2MCgMBuLsBbyQEHwwYdJRgZn8HV3GcUxLYdkxEklbgXX9YyUpslyYxNBMExTd2ABpYBcQAeRoScuAYYAAB4ABUlEneSQHOHa9GD2RjZuYO1DHIhxF7WQ3bgTX47gROAFoAH4+Fjt2hdAKPKD0cP87gDO+AQ68Z00OPPcxRPEldkZv0+H2-aYAOQ7DiPy5j5vNlbpPXGGLuB5rz3UB7-2g9DuBJxHEdjA7r2plBBe+6Xwey5UCuc9H8fk7gLfQRnj3vd9xeZ9X9eXe2e5Hj9VAb70Igp4Qbu773wOrtzYYFkEAm4JQsgU0FkoLshQ4FHDsKEPamxzxjlsJsJ+Hd-iAjsJ-Vwl8YC737mArWoCX6Ygge0XIGg4CwPgVARBfB0GoOQU-UggwRhjEmMeOAAAmBYVAUDwE+BRRocBwRiKOuTLE3IUC7UiJKU45x7wQDAEqYCjQ7j4DBLsHEgihCtStmcG2hpiZ+iWKCcE7kjqSBhPCDg3VkRnDSvgE2xw0A2ActiOe7ChiTiwDnAI-h6gMDsM4OoOIrg3B0RsdxqBPHxjgNeOSExcrwE0SoLEMA4kJLeHo4Jpi35Kl8RHcwRMim8gfL0YOOSXGoDVrkPg2cSEki5iUig1soD4DMaTGAIch5Hz0NnHp8Y8EAB9vFjxuNg+wn8RxdhxEXJQyTLhIWDgM6OkzMQjIMHg6uwwjgHmUagQB5CbgkjIWbcBbR7aC1kInEcsgAAMQs67AAblAQcbSOGclqKsWppMiAXPVmoUeQsxkTIuZQ25dhQU4jUMYH51JOn4C7CQ7Jth4kuMIAQIgcMdnIDhnAauGKPEuK7Cs1Aw4+CkqxUqYcqBEqoGUi4GZdhEVDAoE7DhTsSmTl4YE3Rc9UbRLuLEzFuS0BJJSeMNJOhJSEvgKgUJ4Tzi7XGO5SlmcrhqN5FwfEZS4AEqqcAGpEq6kNPsE0lu0SzlazaRQY1fT1mH02dnNlcyFmeyWdKy4hcULvIuIOPgbLLZOv6a64+wyKm7LnvMxZOdllHCQQc44SiLgnJaSAkh0KoGwvuUnZ5ahg31GyTgxIjqY3aIjZHQZWybgEo-sKiZ7qy2zLjV6zYPrKVrI2VGm1Rqq1NtxMSw4aazgZtOVc0hOabl5tBQ8otcA3kfK+cZCgfzxVksBaQkFYK4AQtIbmjo+b4WWwoBwwq3CZgAGZOIotoeigFlTcV4oJUSklz6UAUuTdSrddLeQMqZSyvQob9ImA6cYrpGxHamAJBYY1DBqkAttAcsElq7DWqmRsO1rTjIPqdShtDAQuyRDBCGttdh+YUYBJ0Ig6sHXIqg-gAAhA0Foc6T1wAAGTcYkDiogx7cijrIwEZdE5+jTU0POUTK5wNOyAA

tidal charm
cerulean ether
#

what

tidal charm
# cerulean ether what

I want to have two signatures, one when the key doesn't have a function as return and another that has a function as return, if it has a function you must pass an object with the translation keys

vapid isleBOT
#
0xc0c0a#0

Preview:```ts
export const i18n = {
defaultLocale: "es",
locales: ["es", "en"],
} as const;

export const languages = {
en: "English",
es: "Español",
};

export const defaultLang = i18n.defaultLocale;
export const showDefaultLang = false;

// Problem 1: I want to type all Parameters of the function as {keu:string} without lose the autocompletation of the keys
...```

cerulean ether
#

@tidal charm

tidal charm
#

@cerulean ether wow thanks! But I found that with the new definition of UI typescript doesnt suggest the translations keys and with const it do it 🤔

cerulean ether
#

ok so, turns out this is a much more complicated thing

#

because i ran into a different issue

#

parameters are contravariant

cerulean ether
#

@tidal charm yk what

#

i have been thinking

#

about this

#

for like

#

a good few hours now

#

and it hit me

vapid isleBOT
#
0xc0c0a#0

Preview:```ts
export const i18n = {
defaultLocale: es,
locales: [es, en],
} as const;

export const languages = {
en: English,
es: Español,
};

type LConfP = Record<
string,
| string
// I honestly don't know what to do here other than never
| ((props: never) => string)
...```

tidal charm
# vapid isle

@cerulean ether wow that it is so impressive, and thanks so much for your time ❤️ , there is a lot of things that I dont understand hahaha.

What is the difference/advantages between LConf and as const?
What is createCompiledTranslation? It's weird because it only has types but it's a function 🧐
Also I found that t has an error => This overload signature is not compatible with its implementation signature.

cerulean ether
#

its basically an imo better version of what you are trying to do and comes with runtime benefits as well

#

the fundamental problem comes with trying to type hint arbitratry property values in the parameter object destructuring

tidal charm
#

On the other hand, I cannot change the function for the compiled version because it's a function that Astro uses for translations, and it would be a breaking change. However, the function can be updated without breaking the code. But it's really interesting 🧐

cerulean ether
#

oh

tidal charm
cerulean ether
tidal charm
cerulean ether
#

#1215142734600339506 btw read its a question i asked because of this question

tidal charm
#

I will check it thanks!

tidal charm
#

@cerulean ether hi, I am trying the computed version that you made but I dont understad because if I only copy that part of the code it said that is not a function when I tried to use so, what it do exactly? 😅

cerulean ether
#

implementing is quite trivial

tidal charm
#

yes it only make this

const useTranslations = (lang) => ui[lang]
#

but what is the difference between type that and use the computed version?

cerulean ether
#

well its mostly up to preference but imo it does many things

#
  1. it simplifies the object you are working with
#
  1. key properties are calculated ahead of time
#
  1. in this process you can also catch invalid keys