希望函数参数是具有动态属性的对象键。

huangapple go评论81阅读模式
英文:

Expect function Parameter to be Key of Object with Dynamic Properties

问题

我想制作一个多语言应用程序。

我希望尽可能严格和简单地构建输入。我的代码如下:

//=== 在我的 Hook 内部: ===//
interface ITranslation {
   [key:string]:[string, string]
}

const useTranslator = (translations:ITranslation) => {
   const language = useLanguage() // 从另一个 Hook 获取语言设置

   const translate = (key:keyof typeof translations) => {
      // 映射并返回正确的翻译
   }

   return translate;
}


//=== 在组件内部: ===//
const translation:ITranlation = {
   "something in english": [ "something in german", "something in spanish" ],
   "anotherthing in english": ["anotherthing in german", "anotherthing in spanish"]
}

const translate = useTranslation(translation)

return(
   <Text>{translate("something in english")}</Text>
)

我想要实现的目标:

  • 当传递包含动态键的 translation 对象到 Hook 中时:useTranslation(translations),应该有一个类型检查来验证是否提供了两种语言(任何属性都具有包含两个字符串的数组)。

  • 当使用翻译函数(在 Text 组件内部)时,如果键不匹配 translations 对象内的动态键,则 TypeScript 应该引发错误。因此,这应该引发错误:tranlate("not a key in object")

但是我无法使其正常工作。我可以将 translations 对象设置为 as const,但是这样一来,将对象传递给 Hook 时就没有类型检查。或者我按上面的方式设置为 translation:ITranslation,但是在组件内部的 translate 函数的参数中就没有类型检查。

是否可能实现这一点?(如果是,如何实现?)

先行致谢!

英文:

Im making an application multi Language.

I want to build typing as strict and simpel as possible. My Code is the following:

//=== Inside my Hook: ===//
interface ITranslation {
   [key:string]:[string, string]
}

const useTranslator = (translations:ITranslation) =&gt; {
   const language = useLanguage() // just getting the language setting from another hook

   const translate = (key:keyof typeof translations) =&gt; {
      // mapping and returning the right translation
   }

   return translate;
}


//=== Inside the component: ===//
const translation:ITranlation = {
   &quot;something in english&quot;: [ &quot;something in german&quot;, &quot;something in spanish&quot; ],
   &quot;anotherthing in english&quot;: [&quot;anotherthing in german&quot;, &quot;anotherthing in spanish&quot;]
}

const translate = useTranslation(translation)

return(
   &lt;Text&gt;{translate(&quot;something in english&quot;)}&lt;/Text&gt;
)

What i want to achieve:

  • When passing the translation Object, with Dynamic Keys to the Hook: useTranslation(translations), there should be a typecheck validating, that both languages are provided (any property has an Array with 2 Strings)

  • When using the translate function (inside the Text component) typescript should bring an error, if a key is not matching the Dynamic Keys inside the translations object. So this should throw an error: tranlate(&quot;not a key in object&quot;)

But i can't get it to work properly. I can either set the translations object as const, but then there is no typecheck when passing the object to the Hook.
Or i set it as shown above with translation:ITranslation but then there is no typechecking for the parameter in the ´translate´ function inside the component.

Is it possible to achive that? (If yes, how?)

Thanks in advance!

答案1

得分: 1

这个解决方案仅适用于 TypeScript >= 4.9,因为它使用了在 4.9 中引入的satisfies操作符

我们将采用添加 as const 的方法,并且 satisfies 将允许我们进行类型检查。

const translation = {
  'something in english': ['something in german', 'something in spanish'],
  'anotherthing in english': ['anotherthing in german', 'anotherthing in spanish'],
} as const satisfies ITranslation;

由于我们添加了 as constITranslation 中的值将变为 readonly [string, string],因此我们必须更新 ITranslation 如下:

interface ITranslation {
  [key: string]: readonly [string, string];
}

接下来,我们需要为 useTranslator 添加一个泛型参数,以便它能够在 ITranslation 的特定实例上工作。对于 translate 函数也是如此。它应该接受关于 ITranslation 键的泛型参数,并返回该特定键的值:

const useTranslator = <T extends ITranslation>(translations: T) => {
  const language = useLanguage(); // 从另一个钩子中获取语言设置

  const translate = <K extends keyof T>(key: K): T[K][number] => {
    // 返回检索到的值
  };

  return translate;
};

由于问题中没有要求,translate 将返回特定键的翻译的联合类型,这是通过 T[K][number] 实现的。

用法:

const Component = () => {
  const translate = useTranslator(translation);
  
  // "something in german" | "something in spanish"
  const case1 = translate('something in english');

  // "anotherthing in german" | "anotherthing in spanish"
  const case2 = translate('anotherthing in english');

  return null;
};

播放器链接

英文:

This solution will work only for Typescript &gt;= 4.9 since it uses the satisfies operator introduced in the 4.9.

Adding as const is the approach we will go with, and satisfies will allow us to type-check it.

const translation = {
  &#39;something in english&#39;: [&#39;something in german&#39;, &#39;something in spanish&#39;],
  &#39;anotherthing in english&#39;: [&#39;anotherthing in german&#39;, &#39;anotherthing in spanish&#39;],
} as const satisfies ITranslation;

Since we added as const the values in the ITranslation will be readonly [string, string], thus we have to update the ITranslation to the following:

interface ITranslation {
  [key: string]: readonly [string, string];
}

Next, we need to add a generic parameter to useTranslator so it works over the specific instance of ITranslation. The same goes for the translate function. It should accept the generic parameter for the key of ITranslation and return the value for that specific key:

const useTranslator = &lt;T extends ITranslation&gt;(translations: T) =&gt; {
  const language = useLanguage(); // just getting the language setting from another hook

  const translate = &lt;K extends keyof T&gt;(key: K): T[K][number] =&gt; {
    // return retrieved value
  };

  return translate;
};

Since it is not asked in the question translate will return a union of the translations for the specific key, which is achieved by T[K][number]

Usage:

const Component = () =&gt; {
  const translate = useTranslator(translation);
  
  // &quot;something in german&quot; | &quot;something in spanish&quot;
  const case1 = translate(&#39;something in english&#39;);

  // &quot;anotherthing in german&quot; | &quot;anotherthing in spanish&quot;
  const case2 = translate( &#39;anotherthing in english&#39;);

  return null;
};

playground

huangapple
  • 本文由 发表于 2023年6月1日 18:20:46
  • 转载请务必保留本文链接:https://go.coder-hub.com/76380911.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定