What is the process for designing a mapping type that involves two enums?

I need a solution to connect these two types and create mappings:

type MappedEnum<T extends string, A extends string, B extends string> = {
  [key in T]: {[key in A]: B};
  [key in T]: {[key in B]: A};
};

enum Greek {
  ALPHA = 'A',
  BETA = 'B',
}

enum English {
  A = 'A',
  B = 'B',
}

enum categories{
  TO_ENGLISH = 'toEnglish',
  TO_GREEK = 'toGreek',
}

const mappedValues: MappedEnum<Types, Greek, English> = {
  toEnglish: {
    [Greek.ALPHA]: English.A,
    [Greek.BETA]: English.B,
  },
  toGreek: {
    [English.A]: Greek.ALPHA,
    [English.B]: Greek.BETA,
  },
};

How can I adjust MappedEnum to make it functional?

Update

Is there a way to establish a TransformedMapping and wrap it to match the instructions below?

type TransformedMapping = {
  toGreek: Greek,
  toEnglish: English,
};

type Mapping = WrapMapping<TransformedMapping>;

// type Mapping = {
//   toGreek: MappedEnum<Greek, English>,
//   toEnglish: MappedEnum<English, Greek>,
// };

Answer №1

The issue lies in the fact that the types enum may not always have just two members. It is also unclear which member corresponds to English to Greek mapping and vice versa. How would this function if types had only one or three members?

An alternative approach is to eliminate the first generic type parameter and rely on hardcoded property names instead, for example:

type MappingForEnum<A extends string, B extends string> = {
  convert: {[key in A]: B};
  reverse: {[key in B]: A};
};

...

const mapping: MappingForEnum<Greek, English> = {
  convert: {
    [Greek.ALPHA]: English.A,
    [Greek.BETA]: English.B,
  },
  reverse: {
    [English.A]: Greek.ALPHA,
    [English.B]: Greek.BETA,
  },
};

Alternatively, you can break down the type into smaller one-way mappings and then combine them:

type MappingForEnum<A extends string, B extends string> = { [key in A]: B };

type GreekEnglishMapping = {
  toEnglish: MappingForEnum<Greek, English>;
  toGreek: MappingForEnum<English, Greek>;
};

...

const mapping: GreekEnglishMapping = {
  toEnglish: {
    [Greek.ALPHA]: English.A,
    [Greek.BETA]: English.B,
  },
  toGreek: {
    [English.A]: Greek.ALPHA,
    [English.B]: Greek.BETA,
  },
};

By using the second solution, it becomes simple to obtain a union type similar to the original types enum:

type supportedMappings = keyof GreekEnglishMapping; // "toEnglish" | "toGreek"

Here's another method:

type MappingForEnum<A extends string, B extends string> = { [key in A]: B };
type TwoWayMapping<T extends { [0]: string, [1]: string }, A extends string, B extends string> =
    { [key in T[0]]: MappingForEnum<A, B> } &
    { [key in T[1]]: MappingForEnum<B, A> };

type GreekEnglishMapping = TwoWayMapping<[ 'toEnglish', 'toGreek' ], Greek, English>;

Unfortunately, even if you define

enum MappingNames {
  toEnglish,
  toGreek,
}

where MappingNames[0] is defined during runtime, the compiler may not recognize it, causing the following to fail:

type GreekEnglishMapping = TwoWayMapping<MappingNames, Greek, English>;
// Type 'MappingNames' does not satisfy the constraint '{ [0]: string; [1]: string; }'.

One more approach:

type MappingForEnum<A extends string, B extends string> = { [key in A]: B };
type TwoWayMapping<T extends { [k: string]: A | B }, A extends string, B extends string> = { 
    [key in keyof T]: T[key] extends A ? MappingForEnum<A, B> : MappingForEnum<B, A>
};

type PureMapping = {
  toEnglish: Greek,
  toGreek: English,
};
type GreekEnglishMapping = TwoWayMapping<PureMapping, Greek, English>;

This is likely as close as you can get to your original requirements. It seems inevitable to specify Greek/English twice (once in the mapping type and once as generic arguments).

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Error Encountered with vscode.workspace.workspaceFolders. Troubleshooting VS Code Extensions

While developing an extension for vscode, I encountered an error that I'm having trouble resolving. The goal is to create a script that generates a new file based on user inputs, but I'm running into an issue when trying to retrieve the path for ...

Assign a specific value to each object

Receiving data from the backend in a straightforward manner: this.archiveService.getRooms(team).subscribe( res => { this.form = res; this.form.forEach(el => { el.reservation.slice(-6).match(/.{1,2}/g).join('/'); }); }, ...

Bringing TypeScript modules from a local module into a React application

As I work on organizing my projects and keeping logic separate in components that will eventually be published, I have a specific structure set up for now: I have a library of Typescript scripts within a project named project-a A separate React app create ...

Using custom properties from the Material-UI theme object with custom props in Emotion styled components: a step-by-step guide

I have implemented a custom object called fTokens into the MUI theme using Module augmentation in TypeScript This is how my theme.d.ts file is structured declare module "@mui/material/styles" { interface FPalette { ... } interface FTokens ...

Issue encountered while creating Next.js 13.4 application: The type 'ResolvingMetadata | undefined' does not meet the requirement of 'ResolvingMetadata'

I'm currently working on dynamically generating metadata for my Next.js 13.4 application using this code snippet: export async function generateMetadata( { params, searchParams }: Props, ) { try { // extract the route parameters ...

Encountering an error while trying to import JSON in TypeScript

I am in need of using mock JSON data to test the rendering of my front-end. import MOCK_FAQ from '../../mocks/FAQ.json'; However, when attempting to import the file, I encountered this exception: Cannot find module '../../mocks/FAQ.json&a ...

What steps can I take to perform unit testing on a custom element?

While working on a project where I have a class that extends HTMLElement, I came across some interesting information in this discussion: https://github.com/Microsoft/TypeScript/issues/574#issuecomment-231683089 I discovered that I was unable to create an ...

Leverage the generic types of an extended interface to simplify the creation of a shorthand type

Attempting to streamline my action shorthand that interacts with AsyncActionCreators. A function has been crafted to accept a React dispatch: Dispatch<T> parameter: const fetchProfileAction = actionCreator.async<void, Profile, any>('FETC ...

Prisma unexpectedly updates the main SQL Server database instead of the specified database in the connection string

I have recently transitioned from using SQLite to SQL Server in the t3 stack with Prisma. Despite having my models defined and setting up the database connection string, I am encountering an issue when trying to run migrations. Upon running the commands: ...

Updating Angular 8 Component and invoking ngOninit

Within my main component, I have 2 nested components. Each of these components contain forms with input fields and span elements. Users can edit the form by clicking on an edit button, or cancel the editing process using a cancel button. However, I need to ...

What is the best way to combine the attributes of multiple objects within a union type?

I have a clearly defined schema type Schema = { a: { a: 1 } b: { b: 2 } } I am in need of a function that can generate objects that adhere to multiple schemas. function createObject<K extends keyof Schema>(schema: Array<K>, obj: Sche ...

Does Vetur have additional undefined types in the type inference of deconstructed props?

When reviewing the code below, Vetur concluded that x,y are of type number | undefined. The presence of undefined is leading to numerous warnings when using x,y further in the code. Is there a way to eliminate the undefined from the type inference? <s ...

Creating a signature for a function that can accept multiple parameter types in TypeScript

I am facing a dilemma with the following code snippet: const func1 = (state: Interface1){ //some code } const func2 = (state: Interface2){ //some other code } const func3: (state: Interface1|Interface2){ //some other code } However, ...

Reassigning Key Names and Types Based on Conditions

How can I modify object key names and properties in a way that allows existing keys and properties to remain the same or be modified (remapped)? My current approach does not properly handle mixed cases: export const FUNC_ENDING_HINT = "$func" as const; ty ...

Probability of an event occurring when represented as whole numbers in percentage form

Currently, I'm developing a unique job system within a Discord bot that allows users to mine various types of ores. The probability of receiving specific ores is based on the user's mining skill level, which is stored in a database and can vary a ...

New substitute for extending the extent in OpenLayers 4

I am currently in the process of migrating my code from OpenLayers 3 to OpenLayers 4 using TypeScript. Previously, I had a code snippet that extended the extent of my map so that all vector shapes I drew would be visible upon loading... angular.forEach(w ...

I need to find a way to dynamically filter a Json object, taking into account that my filter condition may vary. The number of possible scenarios

I need to dynamically filter my data based on changing conditions. For example, I want to call a method on the <select (change)="filterData($event.target.value,'jobStatusId')" >, but the condition to filter by can be dynamic, such ...

Declaration in Typescript for an array of strings that will be returned as a

I am facing an issue with my async function that is supposed to return either a single string or an array of strings. Here is the relevant code snippet: async getAllAnnotationTimes(): Promise<string> | Promise<string[]> { return aw ...

Having trouble looping through an array in Angular 2?

I am currently using a FirebaseObjectObservable to retrieve the value of a property from my firebase database. The property can have multiple values, so I stored them in a local array variable. However, I ran into an issue while trying to iterate through ...

Modifying a property in a nested layout through a page in Next.js 13

Currently, I am facing a challenge in updating the page title within a nested layout structure. app/docs/layout.tsx: export const DocsLayout = ({children}:{children: React.ReactNode}) => { return ( <> <header> ...