A robust method for converting object properties to their specified InstanceType or leaving them unchanged

I am trying to create a method named Instantiate in TypeScript that will accept an object of any type.

If the object contains any properties that are classes, then the method should instantiate those classes and the return type of those properties should be the InstanceType of the respective class.

If the property is not a class, it should remain unchanged.

My issue lies in defining the correct return type. The values do not match the keys of the original object.

I need help with the TypeScript typing for this method, but I will handle the body/implementation on my own.

Here is what I have tried so far:

class Person {
    name!: "John Doe"
}

class Cat {
    scream!: "Help!!!"
}

const configApp = {
    isComplicated: true
}

const testData = {
    person: Person, 
    cat: Cat, 
    config: configApp,

} as const

type TClass = abstract new (...args: any) => any;
type TInstances<OBJ> = Record<
    keyof OBJ, OBJ[keyof OBJ] extends TClass
        ? InstanceType<OBJ[keyof OBJ]>
        : OBJ[keyof OBJ]
>;

const instantiate = <T ,>(obj:T): TInstances<T> => { return {} as any}

// Below should not error
const result = instantiate(testData);
result.person.name; //  Error
result.cat.scream;  // Error
result.config.isComplicated; // Error

Try it out here!

Answer №1

It appears that you are looking to define the type for instantiate as follows:

declare const instantiate: <T extends object>(obj: T) =>
    { [K in keyof T]: T[K] extends new () => infer I ? I : T[K] }

This defines a mapped type over the properties of T, using conditional types to check if each property can be treated as a no-arg constructor. If it is, the property type is replaced with its instance type (as inferred from the original property type).

This code snippet aligns with the desired behavior outlined in your example:

const result = instantiate(testData);
/* const result: {
    readonly person: Person;
    readonly cat: Cat;
    readonly config: {
        isComplicated: boolean;
    };
} */

Therefore, this addresses the initial query.


However, implementing the logic for instantiate accurately may prove challenging. It requires runtime capability to identify whether a given value can serve as a no-arg constructor, allowing you to potentially call new value() for instantiation. Unfortunately, there isn't a foolproof method to determine if something functions as a constructor. While checking if a value is a function and has a zero-length property could hint at its eligibility, not all functions can be constructors, like arrow functions. This uncertainty poses risks during execution based on how your instantiate implementation handles such scenarios.

The scenario mentioned might fall into specific cases that may not concern you directly, but awareness is crucial.

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

I am looking for guidance on removing the bottom line from the ionic 4 segment indicator. Any advice or tips on

.segment-button-indicator { -ms-flex-item-align: end; align-self: flex-end; width: 100%; height: 2px; background-color: var(--indicator-color); opacity: 1; } I am a beginner in hybrid app development and ...

Adding extra fields to an existing JSON response in a TypeScript REST API

I am in need of an additional column to be added to my response data. Currently, I am fetching data from multiple REST endpoints one by one and merging the results into a single JSON format to display them in an Angular Mat table. The columns that I want t ...

Cannot get Typescript Module System configuration to function properly

I'm encountering an issue with my existing TypeScript project where I'm trying to change the module type to System, but despite setting it in the project properties, the compiler always outputs in AMD format (define...). It's frustrating be ...

Using TypeScript with React and Material-UI: Issue with undefined theme in createStyles()

Currently, I am delving into React with TypeScript and utilizing the Material UI framework for the frontend. In my quest to activate media queries, an error has crossed my path: Uncaught TypeError: Cannot read property 'up' of undefined ...

Vue 3 Electron subcomponents and routes not displayed

Working on a project in Vue3 (v3.2.25) and Electron (v16.0.7), I have integrated vue-router4 to handle navigation within the application. During development, everything works perfectly as expected. However, when the application is built for production, the ...

Attempting to showcase information on the Angular frontend

When attempting to retrieve the Street name, I am seeing [object Object]. What is the optimal approach for displaying JSON data on the client side? I managed to display a street name but struggled with other components. How can I access the other elements ...

Error: monaco has not been declared

My goal is to integrate the Microsoft Monaco editor with Angular 2. The approach I am taking involves checking for the presence of monaco before initializing it and creating an editor using monaco.editor.create(). However, despite loading the editor.main.j ...

Creating instances of a child class in Typescript using a static method in the parent class, passing arguments and accessing other static methods

Struggling with instantiating a child class from a static method in a base class. Looking to properly specify the return type instead of resorting to using any for all static methods. Tried a solution here, but it falls short when dealing with static metho ...

Issues with the typings for the toPromise function in WebStorm have been identified

I'm encountering an issue with WebStorm not recognizing the typings for the toPromise function on 'rxjs', despite having updated it. Is there a way I can troubleshoot this and fix it? Strangely, the code still runs successfully despite the ...

How can we effectively map Typescript Enums using their keys without relying on a Map?

Consider the following Enum instances: export enum TopicCategories { GUIDES = 'Guides', TASKS = 'Tasks', CONCEPTS = 'Concepts', FORMULAS = 'Formulas', BLOGS = 'Blogs' } export enum Top ...

Creating Angular UI states with parameters in TypeScript

My Understanding In my experience with TypeScript and angular's ui state, I have utilized "type assertion" through the UI-Router definitely typed library. By injecting $state into my code as shown below: function myCtrl($state: ng.ui.IStateService){ ...

Utilize systemJs to import a node module in a TypeScript file

I attempted to import a node module called immutablejs into my TypeScript file: /// <reference path="../node_modules/immutable/dist/immutable.d.ts" /> import { Map } from 'immutable'; var myMap = Map(); Here are the script tags in my inde ...

Switch from using getElementById to useRef in React components

There is a requirement to update a functional component that currently uses getElementById to instead utilize the useRef hook. The original code snippet is as follows: import React, { useState, useEffect, useRef } from 'react'; import { createPo ...

What is the best way to test a callback function of a React component that is encapsulated with withRouter()?

In my Jest and Enzyme testing of a TypeScript-written React project, I have a component wrapped in a React-Router router. Here's a snippet of the code: import { SomeButton } from './someButton'; import { RouteComponentProps, withRouter } fr ...

essential elements for mapping an array using typescript

Struggling to iterate through an array of objects, each with a unique id created by uuidv4(), resulting in a string type. TypeScript is giving errors (( Type 'String' is not assignable to type 'Key | null | undefined')) due to the &apos ...

The ListBuckets command in AWS S3 provides a comprehensive list of all available

I'm currently working on a TypeScript application that interacts with an AWS S3 bucket. The issue I'm facing is that my current credentials only allow me to read and write data to specific buckets, not all of them. For example: ListBuckets retu ...

Creating Mongoose models in TypeScript with multiple connections

Attempting to build a model with multiple connections as per the documentation, I encountered the following error: TS2345: Argument of type 'Schema<Document<any>, Model<Document<any>>>' is not assignable to parameter of ty ...

NativeScript Error Code NG8001: Element 'ActionBar' is unrecognized

In my project, the startupscreen module setup is as follows: import { NativeScriptFormsModule } from "@nativescript/angular"; import { NativeScriptCommonModule } from "@nativescript/angular/common"; import { NgModule, NO_ERRORS_SCHEMA } ...

Tips for invoking a function from one React component to another component

Currently, I am working on two components: one is Game and the other is PickWinner. The Game component serves as the parent component, from which I need to call the pickWinner function in the PickWinner component. Specifically, I want to trigger the startP ...

Transmitting form data inputted by the user to a modal that resides in the same component, all without the need for child or parent components or

In need of a solution where users can input answers to questions and have all the entered data displayed in a popup alongside the respective question. If a user chooses not to answer a question, I do not want that question or any related information to be ...