Remapping compound enum-like constant objects dynamically with type safety in TypeScript

I am currently developing a small utility that generates typesafe remapped object/types of compound enums. The term "compound" in this context refers to the enum (essentially a const object) key mapping to another object rather than a numeric or literal value. Here is a playground with the code you can check out.

type CompoundRecord<T> = Record<keyof T, Record<keyof T[keyof T], T[keyof T][keyof T[keyof T]]>>;

const enumKeyName = '$$key';
type KeyOfEnumValue<T> = keyof T[keyof T];
type KeyOfEnumKey = typeof enumKeyName;
type KeyOf<T> = KeyOfEnumKey | KeyOfEnumValue<T>

export const remapEnumLike = <
    T extends {},
    IKey extends KeyOf<T>,
    OKey extends KeyOf<T>,
>(
    enumerable: T,
    inKey: IKey,
    outKey: OKey,
) => {
  const remappedEnum = Object.fromEntries(
      (Object.entries(enumerable) as Array<[keyof T, T[keyof T]]>).map(([key, value]) => {

        return [inKey === enumKeyName ? key : value[inKey as KeyOfEnumValue<T>], outKey === enumKeyName ? key : value[outKey as KeyOfEnumValue<T>]];
      })
  );

  return remappedEnum as RemappedEnumLike__wip<T, IKey, OKey> //RemappedEnumLike
};

// Additional code and comments are here, feels free to check them out!

If you have any suggestions or feedback on how to enhance this implementation to make it more efficient or reliable, please share! Thank you!

Answer №1

Utilizing extensive key remapping techniques, you can handle all 4 instances like so:

type RemappedEnumLike<
    T extends CompoundRecord<any>,
    IKey extends KeyOf<T>,
    OKey extends KeyOf<T>
> = IKey extends "$$key"
    ? OKey extends "$$key"
        ? { [K in keyof T]: K }
        : { [K in keyof T]: T[K][OKey] }
    : OKey extends "$$key"
    ? { [K in keyof T as T[K][IKey]]: K }
    : { [K in keyof T as T[K][IKey]]: T[K][OKey] };

Check out these sample calls:

const positionToIso = remapEnumLike(LANGUAGE_CODE, "position", "isoCode");
const ex1 = positionToIso[0]; // "af"

const metaToValue = remapEnumLike(LANGUAGE_CODE, "meta", "value");
const ex2 = metaToValue["عربى"]; // "Arabic"

const keyToValue = remapEnumLike(LANGUAGE_CODE, "$$key", "value");
const ex3 = keyToValue.AF; // "Afrikaans"

const keyToKey = remapEnumLike(LANGUAGE_CODE, "$$key", "$$key");
const ex4 = keyToKey.AF; // "AF"

const valueToKey = remapEnumLike(LANGUAGE_CODE, "value", "$$key");
const ex5 = valueToKey["Afrikaans"]; // "AF"

Playground (with full code)

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

When exporting a custom ES6 module and importing it into a different local project, you may encounter unexpected outputs such as being undefined or

Currently, I am using TypeScript 3.4.5 and Webpack 4.32.2 on Windows 10 via WSL. My goal is to create a local package of tools that consolidates basic classes into an index file for exporting. However, when I try to import these classes into other project ...

Ways to ascertain whether a user has successfully logged in

Just diving into Angular testing and decided to test out the checkLogin function within my application. import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router'; import {AuthenticationService} from &qu ...

Issue with API/CORS not fetching while utilizing react-email in ASP.NET/React.JS application

My React App is running at port 44411 and react-email is running at port 3000. I followed a tutorial on setting up react-email, but it didn't work initially. After some troubleshooting, I managed to make my API request go through Postman. The next st ...

Exploring the Angular TypeScript Method for Rendering Nested Objects in an Array

Within this array, I have a collection of objects that contain properties for model and color. My goal is to present these objects in a table format, with individual access to the color values. grp = [ {model: "CF650-3C", color: {Orange: 3, Black ...

Error: Attempting to access 'pageContext' property on undefined object, resulting in TypeError while utilizing sp pnp v3

I am currently following a tutorial to build a webpart using SPFX and SP/PNP v3: https://learn.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/guidance/use-sp-pnp-js-with-spfx-web-parts I have also consulted: Here is the main .ts file: public async onIn ...

Using Typescript and ThreeJS, include new elements to the environment within the loader

Can someone help me with the following code snippet? export class LandingPageComponent implements OnInit { scene: THREE.Scene; (...) ngOnInit() { this.scene = new THREE.Scene(); var loader = new THREE.JSONLoader(); loader.load("../../assets/fire_lion.j ...

Application: The initialization event in the electron app is not being triggered

I am facing an issue while trying to run my electron app with TypeScript and webpack. I have a main.ts file along with the compiled main.js file. To troubleshoot, I made some edits to the main.js file to verify if the "ready" function is being called. ...

Enclose the type definition for a function from a third-party library

I prefer to utilize Typescript for ensuring immutability in my code. Unfortunately, many libraries do not type their exported function parameters as Readonly or DeepReadonly, even if they are not meant to be mutated. This commonly causes issues because a ...

How can I transfer the data from a file to upload it in Angular 9 without manually typing it out?

In my Angular application, I have a functionality where users can upload two files for processing on the server. However, I am looking to add a feature that allows users to simply copy and paste the contents of the files into two textboxes instead of going ...

Employ a type as a function in Typescript programming

Looking for a way to convert an ID into an object using a specific type. The type accepts personId as a string parameter and returns either a Person or undefined. export type FindPerson = (personId: string) => Person | undefined; I have multiple person ...

Troubleshooting the NullInjectorError in Angular - Service Provider Missing?

I'm facing an issue in my code where I have buttons that should trigger pop-ups displaying details as a list when clicked. However, every time I click the buttons, I encounter the error mentioned below. It seems like I am unable to access the desired ...

The speed of the OpenLayers web application is significantly hindered when accessed from a mobile device using Android

Although it may seem like a common question that should be closed, I have reached a roadblock and cannot find a solution. I hope to provide enough details to make this question suitable for SO. I am currently working on an OpenLayers web application that ...

The function "useLocation" can only be utilized within the scope of a <RouterProvider> in react-router. An Uncaught Error is thrown when trying to use useLocation() outside of a <Router>

When attempting to utilize the useLocation hook in my component, I encountered an error: import React, { useEffect } from 'react'; import { useLocation } from 'react-router-dom'; import { connect } from 'react-redux'; import { ...

Managing API responses using Redux and Typescript

As a beginner in Typescript, I am struggling to integrate Redux with it. The documentation on using Redux with Typescript is confusing me. I am attempting to fetch data and dispatch it to my reducer for future use, just as I did before adopting Typescript ...

Guide to Injecting Services with Dependencies in Angular 2 (Using Ionic 2/Angular 2/Typescript)

As part of my project, I am developing a sample application that connects to a websocket server in Ionic 2 using Typescript. You can find the repository here. The main requirement is to establish the websocket connection when the application starts up. T ...

What is the best way to reference class variables and methods within a callback function in Typescript?

While working on my Angular project with the Highcharts API, I encountered a situation where I needed to pass a state code to a class level method after drilling down to a specific map location. Below is the snippet of my current code: ngOnInit() { this. ...

Can a single data type be utilized in a function that has multiple parameters?

Suppose I have the following functions: add(x : number, y : number) subtract(x : number, y : number) Is there a way to simplify it like this? type common = x : number, y : number add<common>() This would prevent me from having to repeatedly define ...

One way to incorporate type annotations into your onChange and onClick functions in TypeScript when working with React is by specifying the expected

Recently, I created a component type Properties = { label: string, autoFocus: boolean, onClick: (e: React.ClickEvent<HTMLInputElement>) => void, onChange: (e: React.ChangeEvent<HTMLInputElement>) => void } const InputField = ({ h ...

Managing arrays in local storage with Angular 2+

I seem to be missing a crucial element in my endeavor to save and retrieve an array in local storage within my Angular 4 application. The array is fetched from the server and stored in a variable named 'aToDo' with type 'any', like so: ...

Challenges with overwriting TailwindCSS classes within a React component library package

I just released my very first React component on NPM. It's a unique slider with fractions that can be easily dragged. Check it out here: Fractional Range Slider NPM This slider was created using TailwindCSS. During bundling, all the necessary classe ...