Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Typescript template literal index signature

i’m working on a converter app and have been trying ~ for a while now ~ to successfuly type an object with template literals as an index signature.

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 :

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

type ConversionsOptions<T extends TemperatureUnit> = {
  key: ConversionCategoryType;
  items: Array<ConversionItem<T>>;
  formulas: {
    [U in `${T}_to_${T}`]: (n: number) => number;
  };
};

The object for the conversions looks like that :

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),
  },
};

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<T, U>}`]: (n: number) => number;

in the index signature, but unfortunately, it doesn’t work.

Any ideas how this problem could be solved ?

>Solution :

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

Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading