Typescript 模板文字映射类型

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

Typescript template literal mapped type

问题

以下是您要翻译的代码部分:

我正在开发一个转换器应用程序,一直在尝试着使用模板文字作为映射类型的对象类型。
我想要做的是:在下面的代码中对`formulas`对象进行类型定义,该对象将包含将摄氏度转换为开尔文、开尔文转换为华氏度等的所有公式。

我能够像这样对其进行类型定义:
```typescript
type ConversionsOptions<T extends TemperatureUnit> = {
  key: ConversionCategoryType;
  items: Array<ConversionItem<T>>;
  formulas: {
    [U in `${T}_to_${T}`]: (n: number) => number;
  };
};

用于转换的对象看起来是这样的:

const temperatureCategory: ConversionsOptions<
  'celsius' | 'farenheit' | 'kelvin'
> = {
  key: 'temperature',
  items: [
    {
      key: 'celsius',
    },
    {
      key: 'kelvin',
    },
    {
      key: 'farenheit',
    },
  ],
  formulas: {
    celsius_to_kelvin: (n: number) => n + 273.15,
    celsius_to_farenheit: (n: number) => n * (9 / 5) + 32,
    kelvin_to_farenheit: (n: number) => (n - 273.15) * (9 / 5) + 32,
    kelvin_to_celsius: (n: number) => n - 273.15,
    farenheit_to_kelvin: (n: number) => (n - 32) * (5 / 9) + 273.15,
    farenheit_to_celsius: (n: number) => (n - 32) * (5 / 9),
  },
};

因此,使用这种类型,公式对象的索引是正确的,但允许celsius_to_celsiusfarenheit_to_farenheitkelvin_to_kelvin出现在对象中。

我尝试使用排除(Exclude)实用程序类型,如下所示:

[U in `${T}_to_${Exclude<T, U>}`]: (n: number) => number;

在映射类型中,但不幸的是,它不起作用。

有没有什么想法可以解决这个问题?


<details>
<summary>英文:</summary>

i&#39;m working on a converter app and have been trying ~ for a while now ~ to successfuly type an object with template literals as a mapped type.

What I want to do: type the `formulas` object in the code below, that would contain every formula to convert celsius to kelvin, kelvin to farenheit, etc.

I was able to type it like that :
```typescript
type ConversionsOptions&lt;T extends TemperatureUnit&gt; = {
  key: ConversionCategoryType;
  items: Array&lt;ConversionItem&lt;T&gt;&gt;;
  formulas: {
    [U in `${T}_to_${T}`]: (n: number) =&gt; number;
  };
};

The object for the conversions looks like that :

const temperatureCategory: ConversionsOptions&lt;
  &#39;celsius&#39; | &#39;farenheit&#39; | &#39;kelvin&#39;
&gt; = {
  key: &#39;temperature&#39;,
  items: [
    {
      key: &#39;celsius&#39;,
    },
    {
      key: &#39;kelvin&#39;,
    },
    {
      key: &#39;farenheit&#39;,
    },
  ],
  formulas: {
    celsius_to_kelvin: (n: number) =&gt; n + 273.15,
    celsius_to_farenheit: (n: number) =&gt; n * (9 / 5) + 32,
    kelvin_to_farenheit: (n: number) =&gt; (n - 273.15) * (9 / 5) + 32,
    kelvin_to_celsius: (n: number) =&gt; n - 273.15,
    farenheit_to_kelvin: (n: number) =&gt; (n - 32) * (5 / 9) + 273.15,
    farenheit_to_celsius: (n: number) =&gt; (n - 32) * (5 / 9),
  },
};

So with this type, the formula's object indexes are correct but it allows celsius_to_celsius, farenheit_to_farenheit and kelvin_to_kelvin to be in the object.

I tried to use Exclude utility type like this :

[U in `${T}_to_${Exclude&lt;T, U&gt;}`]: (n: number) =&gt; number;

in the mapped type, but unfortunately, it doesn't work.

Any ideas how this problem could be solved ?

答案1

得分: 2

If we use key remapping, we can use Exclude:

type ConversionsOptions<T extends TemperatureUnit> = {
  formulas: {
    [U in T as `${U}_to_${Exclude<T, U>}`]: (n: number) => number;
  };
};

Playground


We can make a type that gets the duplicates utilizing distributive conditional types:

type Duplicates<T extends string> = T extends T ? `${T}_to_${T}` : never;

and then simply omit the dupes from the mapped type:

type ConversionsOptions<T extends TemperatureUnit> = {
  formulas: Omit<{
    [U in `${T}_to_${T}`]: (n: number) => number;
  }, Duplicates<T>>;
};

Playground


You could also inline the type if you don't want to create another type that's only used once:

type ConversionsOptions<T extends TemperatureUnit> = {
  formulas: Omit<{
    [U in `${T}_to_${T}`]: (n: number) => number;
  }, T extends T ? `${T}_to_${T}` : never>;
};

[Playground](https://www.typescriptlang.org/play?#code/C4TwDgpgBAKhC2kBOBDYBXJECqA7AlsFALxQBEAxhADYDO+6tZUAPuQGYoAWWuXEhZmzIBrGgDd8uMgG4AsAChFoSFADCAe1ziISeltoB5MMHwGAPDCgQAHsAi4AJrVgJkaTDgLAAfCSgA3opQUOwaSPDo1Ci0AFxBCiEhANrYUFKwUDFQAAYAJAHYAL4A+sAaJQUAojYU1OiOEJYANFDYPkU5ALqxUAAUuL246PAARroAlCR+w2O68olQRQvLiooUBkT2iLoeWGpoEADm4SC9mtq6+rhGJmY35sFQAORUdAy0z6wvnDwO-IQvmxnmJqJJcM9FH5SAkQmEIlEYr1YUk3vRGGUKqDwb0BkMRuMkFNiDMoABqKAAJgA7ABmAB0AEYAKzNJ4hNEfTElX68AHAXGDKCzQnE0kAKn6AE4oAB6KDMqY

英文:

If we use key remapping, we can use Exclude:

type ConversionsOptions&lt;T extends TemperatureUnit&gt; = {
  formulas:{
    [U in T as `${U}_to_${Exclude&lt;T, U&gt;}`]: (n: number) =&gt; number;
  };
};

Playground


We can make a type that gets the duplicates utilizing distributive conditional types:

type Duplicates&lt;T extends string&gt; = T extends T ? `${T}_to_${T}` : never;

and then simply omit the dupes from the mapped type:

type ConversionsOptions&lt;T extends TemperatureUnit&gt; = {
  formulas: Omit&lt;{
    [U in `${T}_to_${T}`]: (n: number) =&gt; number;
  }, Duplicates&lt;T&gt;&gt;;
};

Playground


You could also inline the type if you don't want to create another type that's only used once:

type ConversionsOptions&lt;T extends TemperatureUnit&gt; = {
  formulas: Omit&lt;{
    [U in `${T}_to_${T}`]: (n: number) =&gt; number;
  }, T extends T ? `${T}_to_${T}` : never&gt;;
};

Playground

huangapple
  • 本文由 发表于 2023年3月4日 00:00:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/75629314.html
匿名

发表评论

匿名网友

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

确定