A function that creates a new object with identical keys as the original input object

I am working on creating a function fn() that has the following specifications:

  1. It takes a single argument x which is an object with optional keys "a" and "b" (each field may be numeric for simplicity)
  2. The function should return a new object with the same keys as the input object, but each field should be a specific class (a->A, b->B). The expected behavior is:
  • Calling fn({a: 1, b: 1}) should return {a: A, b: B}
  • Calling fn({b:1}) should return {b: B}
  • Calling fn({}) should return {}

You can use the numeric values in the input object as initialization values for the classes in the output object. Additionally, new fields "c", "d", "e", and so on may be added to this function later.

I am having trouble implementing this on the type level as well as in JavaScript code. The code I have written so far is causing Typescript errors:

  • Type 'string' is not assignable to type 'keyof OutputObject'.ts(2344)
  • Type 'Partial' is not assignable to type 'Pick'.ts(2322)
class A {}
class B {} 

interface InputObject {
a: number,
b: number
}

interface OutputObject {
a: A,
b: B
}

// My failed attempt at writing fn()

const fn = <
    TObj extends Partial<InputObject>,
    K extends keyof TObj
>(x: TObj): Pick<OutputObject, K> => {

    let returnValue: Partial<OutputObject> = {}

    if (x.a) {
        returnValue.a = new A()
    }

    if (x.b) {
        returnValue.b = new B()
    }

    return returnValue
}

Answer №1

TypeScript faces challenges in comprehending the inner workings of the fn() implementation to ensure it aligns with the generic mapped type relationship of Picking identical keys from the OutputObject and InputObject types. To proceed, you'll need to employ type assertions to relax the type checking. This necessitates caution in ensuring your code behaves as intended since the compiler cannot fully verify it.

Here's a potential strategy:

const fn = <K extends keyof InputObject = never>(
    x: Pick<InputObject, K> & Partial<InputObject>
): Pick<OutputObject, K> => {    
    let returnValue = {} as
        Pick<OutputObject, K> & Partial<OutputObject>;
    if (x.a) { returnValue.a = new A() }    
    if (x.b) { returnValue.b = new B() }    
    return returnValue;
}

Key aspects to note include:

  • The function is generic only in K, the keys existing in the x input type. This simplifies the compiler's inference of K, particularly since we aim to avoid any surplus keys that could arise if x were a subtype of Pick<InputObject, K>.

  • When passing in {}, the compiler struggles to accurately infer

    K</code, often reverting to <code>keyof InputObject
    , contrary to our intentions. We seek the never type, indicating the absence of any keys. Therefore, a default type argument of never is established.

  • The input type intersects with Partial<InputObject> within the function to confirm that if a property with key a or b exists, it aligns with the InputObject value type. This precaution is necessary because TS object types are not sealed. For instance, the value {a: 1, b: "hello"} technically qualifies as type

    Pick<InputObject, "a">
    , enabling the creation of
    interface Foo extends Pick<InputObject, "a"> {b: string}
    and utilizing a Foo. We aim to prevent such scenarios.

  • The type assertion for returnValue is initially inaccurate, given that {} is unlikely to match type Pick<OutputObject, K>. However, the assumption is that this will rectify itself by the function's conclusion.

  • The asserted type is also combined with

    Partial<OutputObject></code for the same reason as the preceding intersection, assuring the compiler that indexing into <code>returnValue
    using a and b is acceptable.

Testing time:

const v = fn({ a: 1 });
// const v: Pick<OutputObject, "a">
const w = fn({});
// const w: Pick<OutputObject, never>
const x = fn({ b: 2 });
// const x: Pick<OutputObject, "b">
const y = fn({ a: 1, b: 2 })
// const y: Pick<OutputObject, "a" | "b">

Results align with expectations. The output types match the anticipated values.

Playground link to 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

Utilizing JodaTime with JavaScript (AngularJS): A Comprehensive Guide

I am utilizing DateTime to create Date objects and sending them as JSON to the UI. How can I work with this in JavaScript (specifically AngularJS) and convert it back and forth? For instance, if I need the user to update the time, I should be able to retr ...

Obtain the height and width of the original images from the href and assign them to your attributes using jQuery

Hey there pals, I'm currently on a mission to fetch the dimensions (height and width) of an image from a hyperlink and then insert those values into its attribute. This task has got me going bonkers! Here's my snippet: <figure> <a ...

Typescript: Omitting mandatory fields from a type

Can anyone help me with defining the type ExcludeAllRequiredProps<T> in the code below to exclude all required properties? Any assistance would be greatly appreciated. Thank you. type A = { a: number, b: number, c?: number, d?: number } typ ...

What is causing my controller action to create two new records instead of just one?

My current issue involves a simple Razor view featuring a button designed to add a new record to a table within my SQL database. The button is equipped with an Ajax function that triggers a call to the controller. I've experimented with two different ...

Ways to select the element based on a specific CSS property value in the inline style

As a novice in the world of Javascript, I am currently attempting to select an element within an array of images where the opacity is set to 1. Could someone please guide me on how to achieve this? I've tried using hasAttribute, but I'm unsure ho ...

Adjust parent div size based on image size increase

I am currently facing a situation where I have a page displaying an image, but sometimes it appears too small. In order to make the image larger, I have utilized CSS Transform and it is working well. However, the issue lies in the fact that the parent DIV ...

Leveraging Inversify for implementing a Multiinject functionality for a class with both constant and injected parameters

In my Typescript code, I have an insuranceController class that takes a multi inject parameter: @injectable() export class InsuranceController implements IInsuranceController { private policies: IInsurancePolicy[]; constructor(@multiInject("IInsu ...

Generate a .py file through an HTML button click and save it to your device

My current project involves developing HTML and JavaScript code for a chatbot. I need to implement a feature where, upon receiving a Python program from the chatbot, a download button should be displayed. When users click on this Download button, they shou ...

Encountering a "TypeError: Cannot access the 'state' property of undefined" error while using React's "Map" function

It's clear from the examples below that the first photo functions perfectly when this.state.blabal is NOT located within the map(a, b){blabla} However, as seen in photo2, when I move the functioning block inside the map(a, b){`here!!`} {Object.k ...

Converting JSON data from one structure to a different format

Could you assist me in transforming this JSON data into the desired format specified in the result section? [ { name: "FieldData[FirstName][Operator]", value: "=" } { name: "FieldData[FirstName][Value]", value: "test& ...

Inconsistency with Angular 4 instance variables causes ambiguity within a function

Here is the code snippet: @Component({ selector: 'unb-navbar', templateUrl: './navbar.html' }) export class NavbarComponent implements OnInit { @Input() brand: string; controlador:boolean=false; overlay:string=""; @Input() menu ...

When utilizing custom modules in Node.js, is it more effective to require the module from within a function or at the top of the script?

I have developed a custom script that includes all of my common settings and functions. One specific function within this script takes a timestamp as input and outputs a human-readable date with a customized format using Moment.js. The custom script is st ...

Expecting expression and a syntax error occurred: JSX nextjs token was unexpected

When rendering a table from an array, I utilized the following code: const Posts: NextPage = ({ posts}: any) => { return ( <> ..... <tbody> { posts.map((post: any) => { const _date = new ...

In React, the edit mode fails to display class attributes

When I attempted to combine both the creation and editing functionalities in one form, I encountered a problem. Specifically, I was able to retrieve the correct ID value when editing an element, but I struggled to access the attribute values. import { yup ...

How can we avoid page flickering and stuttering while scrolling using the ScrollTo script?

Currently, I am utilizing Ariel Flesler's ScrollTo script which can be found on this page. There are two links located at the bottom of the page that when clicked, will scroll to the top of the page. One of the links also triggers a contact form to op ...

Adjusting the dimensions of a rectangle using jQuery: A step-by-step guide

I am encountering an issue while attempting to adjust the width and height of a rectangle using jQuery. The error message I receive is "Uncaught TypeError: Cannot read property 'scrollWidth' of null" Below you can find the code snippet in questi ...

What is the best method for excluding past dates in the Ui calendar?

As a beginner with Ui calendar, I am seeking guidance on how to prevent users from selecting or interacting with previous dates in Ui-calendar using angularjs. While the Eventdrop, EventResize, and eventclick features are functioning properly for me, it ...

Ways to retrieve the innerHTML content in Mozilla Firefox

Look at this HTML code snippet: <div id="divTest"> Hello <input type="text" id="txtID" value="" /> <input type="button" onclick="getForm();" value="Click" /> </div> Also, check out this JavaScript function for retrievi ...

How can we dynamically reference past elements in an array without using indices like "i-1", "i-2", and so on?

function play_tune(melody) { let played_notes = []; let durations = []; let positions = []; let wait_time = 0; let time_passed = 0; console.log("Melody initiation.") for (let key in melody) { played_notes.push(melody[key].note); dur ...

How can I use regular expressions to validate one-letter domain names?

I am in the process of developing a validation rule for my C# MVC Model using Regex. [RegularExpression(@"(\w[-._+\w]*\w@\w{1,}.\w{2,3})", ErrorMessage = "* Email Address: Please enter a valid Email Address.")] public virtual stri ...