Typescript: 忽略已翻译的属性

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

Typescript: Omitting translated properties

问题

我有可以翻译的接口 - 例如,它们具有 nameDenameFr 属性。目前我正在尝试构建一个函数,该函数仅返回用户语言中具有 name 属性的对象。

到目前为止,我有以下内容 - 它适用于已翻译的属性,但不适用于“未翻译”的属性:

type TranslatedObject<T> = {
  [K in keyof T as K extends `${infer Name}De` ? `${Name}` : never]: T[K extends `${infer Name}De` ? K : never];
};

interface Car {
  nameDe: string,
  nameFr: string,
  price: number
}

const carWithUserLang: TranslatedObject<Car> = {
  name: 'Make Model Trim',
  price: 50000 // --> 类型 '{ name: string; price: number; }' 无法赋值给类型 'TranslatedObject<Car>'。
}

如何使其正常运行?

Stackblitz: https://stackblitz.com/edit/typescript-h7ghdn?file=index.ts&amp;view=editor

英文:

I have interfaces that can be translated - they have the nameDe and nameFr properties for example. Currently I'm trying to build a function that returns the object with name only in the users language.

This is what I have so far - it works for the translated properties but not the "non-translated":

type TranslatedObject&lt;T&gt; = {
  [K in keyof T as K extends `${infer Name}De` ? `${Name}` : never]: T[K extends `${infer Name}De` ? K : never];
};

interface Car {
  nameDe: string,
  nameFr: string,
  price: number
}

const carWithUserLang: TranslatedObject&lt;Car&gt; = {
  name: &#39;Make Model Trim&#39;,
  price: 50000 // --&gt; Type &#39;{ name: string; price: number; }&#39; is not assignable to type &#39;TranslatedObject&lt;Car&gt;&#39;.
}

How can I make this run?
Stackblitz: https://stackblitz.com/edit/typescript-h7ghdn?file=index.ts&amp;view=editor

答案1

得分: 2

我的假设是您希望 TranslatedObject&lt;T&gt; 剥离键名末尾的 &quot;De&quot;&quot;Fr&quot; ,如果存在的话,否则保持不变。 如果是这样,那么这纯粹是一个 键重映 操作,不改变属性值类型。 就像这样:

type TranslatedObject&lt;T&gt; = {
  [K in keyof T as K extends `${infer Name}${&quot;De&quot; | &quot;Fr&quot;}` ? Name : K]: T[K];
};

它使用 条件类型推断模板文字类型 内部剥离任何 &quot;De&quot;&quot;Fr&quot; 后缀。 检查 K extends `${infer Name}${&quot;De&quot; | &quot;Fr&quot;}` 尝试将 K 与以某个任意字符串开头且以 &quot;De&quot; | &quot;Fr&quot; 结尾的内容匹配;如果成功,则推断 Name 是那个任意的初始字符串。 所以结果的键类型在解析成功时会评估为 Name,如果失败则为 K

这导致了您期望的类型:

interface Car {
  nameDe: string,
  nameFr: string,
  price: number
}

type TranslatedCar = TranslatedObject&lt;Car&gt;;
/* type TranslatedCar = {
    name: string;
    price: number;
} */
英文:

My assumption is that you want TranslatedObject&lt;T&gt; to strip &quot;De&quot; and &quot;Fr&quot; off of the ends of key names, if they exist, and otherwise leave them alone. If so then this is purely a key remapping operation that doesn't change the property value types. Like this:

type TranslatedObject&lt;T&gt; = {
  [K in keyof T as K extends `${infer Name}${&quot;De&quot; | &quot;Fr&quot;}` ? Name : K]: T[K];
};

It uses a conditional type inference within a template literal type to strip off any &quot;De&quot; or &quot;Fr&quot; suffix. The check K extends `${infer Name}${&quot;De&quot; | &quot;Fr&quot;}` tries to match K to something that starts with some arbitrary string and ends in &quot;De&quot; | &quot;Fr&quot;; if it succeeds, then Name is inferred to be that arbitrary initial string. So then the resulting key type evaluates to Name if the parsing succeeded, or K if it failed.

That results in the type you were expecting:

interface Car {
  nameDe: string,
  nameFr: string,
  price: number
}

type TranslatedCar = TranslatedObject&lt;Car&gt;;
/* type TranslatedCar = {
    name: string;
    price: number;
} */

Note that there are some interesting edge cases, like what happens if your two keys have different value types:

type Weird = TranslatedObject&lt;{
  oopsDe: string;
  oopsFr: number;
  other: boolean;
}&gt;

/* type Weird = {
    oops: string | number; // &lt;-- becomes union
    other: boolean;
} /

That might be what one would expect, or it might not. The point is that one should always thoroughly test against one's use cases.

Playground link to code

huangapple
  • 本文由 发表于 2023年6月5日 22:05:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/76407242.html
匿名

发表评论

匿名网友

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

确定