In Typescript, convert an object into a different type while maintaining its keys in the resulting type

Imagine you have a code snippet like this

type ResourceDecorator = (input: UserResourceDefinition) => DecoratedResourceDefinition
const decorate: ResourceDecorator = ...

const resources = decorate({
   Book1: {
      resourceName: 'my-book',
      resourceType: 'book'
   },
   Pencil1: {
      resourceName: 'my-pencil',
      resourceType: 'pencil'
   } 
})

The goal is to create the function decorate(...) in such a way that the first-level keys from the input type (Book1, Pencil1) are also present in the output type. This allows using the output resources as shown below

// somewhere else

console.log(resources.Book1.resourceName)

// Additional programmatically defined properties can be accessed along with user definition.
console.log(resources.Book1.exampleDecorationProperty) 

An attempt was made using indexable object syntax, but it did not yield the desired result.

export interface UserResourceDefinition {
    [key: string]: {
        resourceName: string,
        resourceType: string,
    }
}

export interface DecoratedResourceDefinition {
    [key: string]: {
        resourceName: string,
        resourceType: string,
        exampleDecorationProperty: string
    }
}

type ResourceDecorator = (input: UserResourceDefinition) => DecoratedResourceDefinition
const decorate: ResourceDecorator = (input) => {
   return Object.entries(definition).map(([resourceKey, userDef]) => ({
        resourceName: userDef.resourceName,
        resourceType: userDef.resourceType,
        resourceKey: resourceKey,
        exampleDecorationProperty: someFunction(userDef)
   })).reduce((accumObj, decoratedDef) => ({ ...accumObj, [decoratedDef.resourceKey]: decoratedDef }), {});
}

This approach fails because the output type of resources does not recognize properties like Book1 and Pencil1.

// somewhere else

// Auto completion cannot infer 'resources.Book1'
console.log(resources.Book1.resourceName)

// Compiler doesn't flag non-existing property 'Foo'
console.log(resources.Foo.resourceName)

Is there a way to achieve this functionality in Typescript?

Answer №1

My suggestion is to try this approach

const customize = <U extends {}>(data: U): U & {[key in keyof U]: {customProperty:string}} =>
{
    let customizedData = {...data}
    const keys = Object.keys(data) as Array<keyof U>
    keys.forEach(key => customizedData[key] = {...customizedData[key], customProperty:'test'})
    return customizedData as U & {[key in keyof U]: {customProperty:string}};
}

If this doesn't meet your needs, feel free to ask for more assistance!

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

Creating a Show/Hide toggle feature in AngularJS using NG-Repeat

I'm facing an issue with my code where I have a list of items that should only open one item at a time when clicked. However, currently, all items are opening on click and closing on the second click. Can anyone help me identify the problem in my code ...

What is the best way to filter an array based on the property of its inner array?

I am grappling with the following array of objects: let a = [ { b: [1, 11, 12], c: "a1" }, { b: [2, 56, 1], c: "a2" }, { b: [1, 2, 3], c: "a3" } ] In search of an operation that is b ...

Obtain the element created by a directive within the controller

I'm currently utilizing the wrapper located at: https://github.com/Yankovsky/nouislider-angular/blob/master/nouislider.js for the nouislider plugin. Within my controller, I aim to access the element that I've created in the template: <div ya ...

What is the best way to modify an Li element using the DOM in JavaScript?

Just the other day, I discovered how to use JavaScript and DOM to add and delete Li elements. Now I'm curious about how to edit those Li elements using DOM. Any suggestions? Thank you! ...

Is it true that Firefox fails to display javascript errors on AJAX 'threads'?

Currently, I have a function called test_fn that is being called in two ways: (a) Through an onclick event of a button; or (b) from the success callback within a jQuery AJAX request, as shown here: $.ajax({ type: 'POST', ... succes ...

Replicate the function of the back button following the submission of an ajax-submitted form to Preview Form

I am currently working on a multi-part form with the following data flow: Complete the form, then SUBMIT (using ajax post) jQuery Form and CodeIgniter validation messages displayed if necessary Preview the submitted answers from the form Options: Canc ...

Vue.js - Leveraging arrays in functions for efficient iteration

I need help using a JS array in a Vue.js function for looping. Here is the code I tried: Vue: computed: { bbb: function(){ var vvv=this.Preused.length-this.quote.lines.length; let added_products=[]; if(vvv > 0){ for(var i = 0; i <= vvv-1 ...

When utilizing auth0, an error occurs during the grunt build process

I successfully developed a small project using AngularJS on my local machine without encountering any issues. However, when I attempted to build the project with Grunt, everything stopped working and an error occurred: vendor.ce3c01a3.js:2 Error: [$inject ...

Public directory assets not accessible

After working extensively with Node and Angular, I realized my back-end structure needed some serious attention. In an effort to streamline my process, I decided to separate the client and server components and create a reusable skeleton for future applica ...

Jest Test - Uncaught TypeError: Unable to create range using document.createRange

my unique test import VueI18n from 'vue-i18n' import Vuex from "vuex" import iView from 'view-design' import {mount,createLocalVue} from '@vue/test-utils' // @ts-ignore import FormAccountName from '@/views/forms/FormAcco ...

Why isn't pagination typically positioned inside of a tbody element rather than before or after it?

I've created a user table that is based on the number parameter. I added a filter that listens to input and performs an AJAX call each time with the filter applied to the name field. However, the pagination is initially displayed ABOVE the entire ta ...

Guide to placing the cursor at a specified position within an editable content div

I am struggling with a div <div id="user_status" class="status expanddiv" onkeyup="username_Search('New','user_status');" contenteditable="true" data-text="What's on your mind?..."> Some Text will be inserted here. </div ...

Tips for properly panning across the canvas

I added event listeners to capture mouse movement, clicks, and releases in my code. canvas.addEventListener('mousemove', onMouseMove, false); canvas.addEventListener('mousedown', onMouseDown,false); canvas.addEventListener('mouseu ...

Encountering this issue despite confirming the presence of data on the line before! What could be the missing piece here? Error: Unable to access property 'includes' of undefined

Here is the situation.. I'm retrieving data from a database and storing it in an array of objects. These objects represent articles. I am working on implementing a filter system based on categories. The objective is to apply a filter that checks for a ...

I need help figuring out how to get a horizontal scrollbar positioned at the top of a div to function properly once the content has been loaded using ajax

HTML CODE <div id="scrollidout" style="overflow: auto; overflow-y: hidden; "> <div id="scrollidin" style="padding-top: 1px; height: 20px; width: 1000px">&nbsp;</div> </div> <div class="resultcontent" style="overflow ...

The componentDidMount function is not initializing the state

I am trying to access the references from the render function and then set them to the state. Below is my code snippet: class App extends Component { constructor(props) { super(); this.arr = this.generateTimelineArray(); ...

How to include images in a PDF using jspdf without encountering issues with Adobe Reader?

For a project I'm working on, I've integrated jspdf to convert some charts into a PDF. The framework I'm using is angularjs 1.5.6, and the charts are created with chart.js. The HTML snippet for the charts looks like this: <div name="char ...

Using Inline Styling to Showcase a Background Image in a REACTJS Component

import React from 'react'; import Slick from 'react-slick'; import style from './Slider.module.css'; import {Link} from 'react-router-dom'; const SliderTemplates = (props) => { let template = null; const ...

Changing the text of a link when hovering - with a transition

Seeking a straightforward way to change text on a link upon :Hover. I desire a gentle transition (text emerges from below) and fallback to default if JavaScript is disabled. HTML <div class="bot-text"> <a href="">Visit this site</a> ...

The onClick action kicks in as soon as the page finishes loading

One particular line of code in the Button Tag has caught my attention. let content = this.props.searchResult.map((ele, i) => { let card_id = ele.user.username + "_card"; return( <div className="col s12 m6 l4" key={i} id={card_id}> ...