Home > other >  Typescript mapped types - enum value as key, and interface's corresponding value as value
Typescript mapped types - enum value as key, and interface's corresponding value as value

Time:03-29

I have a set of preferences, defined as an enum of strings:

export enum PreferenceTypes {
    language = "language",
    unit = "unit",
}

I can then create an interface to define the shape of an expected object. The keys will be the values of the enum:

export type UnitTypes = "µg/m3" | "ppm" | "ppb";
export type LanguageTypes = "English" | "Spanish";

export interface PreferenceOptions {
    [PreferenceTypes.language]: {
        name: string;
        value: LanguageTypes;
    }[];
    [PreferenceTypes.unit]: {
        name: string;
        value: UnitTypes;
    }[];
}

Now I want to create a default preferences object, based on a user locale. I want the keys of this new object to be the local, and I want the values to be objects. Each object should have keys of type PreferenceTypes, and the values must be the type of value that corresponds to that PreferenceType in PreferenceOptions. I am trying to construct such a type constraint, but I am having a hard time:

PreferenceByLocale: {
    [key: string]: { [key in PreferenceTypes]?: string };
} = {
    /** Defaults for UK users */
    en: {
        language: "English",
        unit: "µg/m3",
    },
    /** Defaults for Spain users */
    es: {
        language: "Spanish",
        unit: "µg/m3",
    },
    /** Defaults for US users */
    us: {
        language: "English",
        unit: "ppm",
    },
};

I dont know how to say that the value of each of these objects should really be { [T extends key in PreferenceTypes]?: PreferenceOptions[T]['value'] } - that gives me TS errors. I am not sure if what I'm trying to do is possible, or if I'm overthinking my typing. For example, I should be able to get an error if I wrote something like this:

PreferenceByLocale: {
    [key: string]: { [key in PreferenceTypes]?: string };
} = {
    /** Defaults for mars users */
    mrs: {
        // I want this to error, as "Martian" does not exist on LanguageTypes
        language: "Martian", 
        unit: "µg/m3",
    },
}

Is such a thing possible?

CodePudding user response:

Ok, I think I understand better what you want to do now. I made a revision like this.

export enum PreferenceTypes {
language = "language",
unit = "unit",
}

export type UnitTypes = "µg/m3" | "ppm" | "ppb";
export type LanguageTypes = "English" | "Spanish";

export interface PreferenceOptions {
[PreferenceTypes.language]: LanguageTypes;
[PreferenceTypes.unit]: UnitTypes;
}

export interface PreferenceByLocale {
[key : string]: PreferenceOptions;
}

const PreferenceByLocale: PreferenceByLocale = {
/** Defaults for UK users */
en: {
    language: "English",
    unit: "µg/m3",
},
/** Defaults for Spain users */
es: {
    language: "Spanish",
    unit: "µg/m3",
},
/** Defaults for US users */
us: {
    language: "English",
    unit: "ppm",
},
mrs: {
    language: "Unkown",
    unit: "sxsx"
  }
 };

 console.log(PreferenceByLocale);

now it gives the following error for mrs:

enter image description here

I guess that's what you wanted to do. If that's what you want to do and the code is hard to understand, I can explain.

CodePudding user response:

Your mistake is: When defining PreferenceOptions, the value of each property is an array. Examine the code below carefully:

export interface PreferenceOptions {
[PreferenceTypes.language]: {
    name: string;
    value: LanguageTypes;
}[];
[PreferenceTypes.unit]: {
    name: string;
    value: UnitTypes;
}[];
}

Your need to know, in TypeScript if you add [] to the end of a value it defines it as an array. So the following two blocks of code give the same result:

let names : Array<string> = [];
let names : string[] = [];

So all you have to do is to change the PreferenceOptions as below:

export interface PreferenceOptions {
[PreferenceTypes.language]: {
    name: string;
    value: LanguageTypes;
};
[PreferenceTypes.unit]: {
    name: string;
    value: UnitTypes;
};
}

and now try this:

const PreferenceByLocale: {
[key: string]: { [key in PreferenceTypes]?: string };
} = {
/** Defaults for UK users */
en: {
    language: "English",
    unit: "µg/m3",
},
/** Defaults for Spain users */
es: {
    language: "Spanish",
    unit: "µg/m3",
},
/** Defaults for US users */
us: {
    language: "English",
    unit: "ppm",
},
};
console.log(PreferenceByLocale)

will give the following result:

{
   en: { language: 'English', unit: 'µg/m3' },
   es: { language: 'Spanish', unit: 'µg/m3' },
   us: { language: 'English', unit: 'ppm' }
}
  • Related