Using TypeScript to pass an entire object to a constructor

I'm working with a TypeScript class:

export class Child {
  name:string;
  age:number;
}

I'm looking to restrict class instances to only have properties declared in the class.

For instance, if I retrieve an object from Firebase:

myFirebaseService.getChild(id).then(function(child){
  var currentChild = new Child(child);
}) 

So when the object is: {name:"ben", color:"db"}, I want the result to be:

currentChild = {"name":"ben"}

Because "color" is not a field of "child".

I attempted the following approach:

export class Child {
  name:string;
  age:number;

  constructor(tempChild:Child = null) {
    if (tempChild){
      for (var prop in tempChild) {
        this[prop] = tempChild[prop];
      }
    }
  }
}

But it doesn't work as expected. The "currentChild" still ends up with all fields attached to the class instance.

(Of course, I could use the following code:

export class Child {
  name:string;
  age:number;

  constructor(tempChild:Child = null) {
    if (tempChild){
      this.name = tempChild.name;
      this.age = tempChild.age;
    }
  }
}

, but since my actual class has numerous fields, I prefer a shorter solution)

Answer №1

We are seeking:

  • To define class fields once
  • To incorporate methods within our class

Our suggested solution:

class Animal {
  name: string = 'default value';
  group: string = 'default value';

  constructor(data: Partial<Animal> = {}) {
    Object.assign(this, data)
  }

  echo() {
    return `My name is ${this.name}, I'm from: ${this.group}`;
  }
}

class Dog extends Animal {
  echo() {
    return super.echo() + ' from Dog class';
  }
}

const dog = new Dog({name: 'Teddy'});
console.log(dog.echo());

Animal - primary class

Dog - secondary class

All functionality operates smoothly without any TypeScript errors

Answer №2

In my humble opinion, the most efficient way to construct your class instances from any object is through destructuring. This approach allows you to assign values to all fields while maintaining precise control over how to handle the absence of a field and which fields should be assigned to your class:

export class Child {
  name: string
  age: number

  constructor({ name = 'default name', age = 0 } = {}) {
    this.name = name
    this.age = age
  }
}

This method enables you to create instances from various types of objects or partial object literals. However, when using literals, it restricts adding extra properties, which aligns with your requirements:

const c0 = new Child({} as any /* Type assertion for Object also works */)
const c1 = new Child({}) // Using defaults for fields
const c2 = new Child() // No arguments passed, defaults used
const c3 = new Child({ name: 'a', age: 1 })
const c4 = new Child({ name: 'b', foo: 'bar' }) // Error due to additional 'foo' property

The resulting JavaScript code automates checks that would typically be performed manually:

define(["require", "exports"], function (require, exports) {
    "use strict";
    var Child = (function () {
        function Child(_a) {
            var _b = _a === void 0 ? {} : _a, _c = _b.name, name = _c === void 0 ? 'default name' : _c, _d = _b.age, age = _d === void 0 ? 0 : _d;
            this.name = name;
            this.age = age;
        }
        return Child;
    }());
    exports.Child = Child;
});

Experiment on the playground to view the generated output!

Answer №3

When working with class definitions in JavaScript, it's important to note that the member definitions made before the constructor may not be properly translated into Javascript by the compiler:

class Child {
    name: string;
    age: number;

    constructor() {
        this.name = "name";
    }
}

After compilation, you'll see that only the name member is retained and used when assigned.

If you need to access these members during runtime, one approach is to store them in an array within the class:

class Child {
    private static MEMBERS = ["name", "age"];

    name: string;
    age: number;

    constructor(tempChild: Child = null) {
        if (tempChild) {
            for (var prop in tempChild) {
                if (Child.MEMBERS.indexOf(props) >= 0) {
                    this[prop] = tempChild[prop];
                }
            }
        }
    }
}

To make this process more manageable, decorators can be utilized. Here's an example of using decorators for defining and accessing members:

function member(cls: any, name: string) {
    if (!cls.constructor.MEMBERS) {
        cls.constructor.MEMBERS = [];
    }

    cls.constructor.MEMBERS.push(name);
}

function isMember(cls: any, name: string): boolean {
    return cls.MEMBERS[name] != null;
}

class Child {
    @member
    name: string;

    @member
    age: number;

    constructor(tempChild: Child = null) {
        if (tempChild) {
            for (var prop in tempChild) {
                if (isMember(Child, prop)) {
                    this[prop] = tempChild[prop];
                }
            }
        }
    }
}

This implementation has not been tested, so its functionality can't be guaranteed. However, if you opt to use decorators, this setup includes most of what you would need.

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

Why aren't enums that can be derived supported?

Is there a way to create an enum that is a subset of another enum? Sometimes it would be useful to have an enum that is a subset of another Enum with the same values at run time but under a different name. Are there better ways to handle this scenario? ...

Error: Unable to locate sportsRecord due to missing function

updated answer1: Greetings, I have updated the question with some detailed records and console logs for better understanding. sportsRecord = { playerTigers:[ {TigerNo: 237, TigerName: "Bird Bay Area", TigerkGroupNo: 1, isDefault: true ...

Can someone provide guidance on effectively implementing this JavaScript (TypeScript) Tree Recursion function?

I'm currently grappling with coding a recursive function, specifically one that involves "Tree Recursion". I could really use some guidance to steer me in the right direction. To better explain my dilemma, let's consider a basic example showcasi ...

Having trouble with injecting string HTML into the head section in NextJS?

We have incorporated a NextJS page with a headless CMS platform. Within this content management system, I have included an option for the administrator to save custom code snippets to be inserted either in the head section or at the end of the body. In m ...

How can we track and record NaN values in JavaScript/TypeScript as they occur in real-time?

Is there a reliable method to identify and prevent NaN values during runtime, throughout all areas of the application where they might arise? A) Are there effective linting tools available to alert about possible occurrences of NaN values within specific ...

The enigma of TypeScript

Whenever I try to declare or initialize data members in a class, the following methods never seem to work: var view: string[]; var view: string[] = []; let view: string[]; let view: string[] = []; Even though the TypeScript documentation states that it s ...

Styling in Svelte/TS does not change when applied through a foreach loop

I've been experimenting with creating a unique "bubble" effect on one of my websites, but I'm facing difficulty changing the styling in a foreach loop. Despite no errors showing up in the console, I'm at a loss as to how to effectively debu ...

"Pairing AngularJS 2 with Vaadin for a winning combination

Good day, I'm currently following a tutorial but encountering some challenges with integrating Vaadin and Angularjs2 into my Joomla Backend project. The error message I am facing is as follows: polymer-micro.html:196 Uncaught TypeError: Cannot read ...

The data type 'string' cannot be assigned to the type 'SystemStyleObject | undefined' in the context of Next.js and Chakra UI

I am currently working on a project that involves Next.js, TypeScript, and Chakra UI. While configuring Button themes in the button.ts file, I encountered an error related to the baseStyle object for properties like borderRadius, color, and border: Type & ...

Creating a custom type declaration for .lottie files in Next.js

https://i.sstatic.net/go6pV.png I've successfully integrated the module with no "can't find module" errors, and my code is functioning correctly. However, the main problem lies in the type declarations error, which prevents my code from recogni ...

The process of transitioning to a different view through a button press

How can I switch to another view by clicking a button in Aurelia? Here is my code: <button class="btn btn-success " click.delegate="save()">New item</button> Typescript configureRouter(config: RouterConfiguration, router: ...

Changing the value within a nested object dynamically in Angular 6 during execution

Currently working with angular 6 and have a method like this: public Save(): void { this.data.station.parkingSlot.forEach(p => { if(p.isAvailable){ p.isAvailable = false; this._carService.addCar(this.data); return true; ...

Step-by-step guide on mocking a method within a method using JEST

Currently, I am facing a challenge in unit testing a method named search. This method consists of three additional methods - buildSearchParameter, isUnknownFields, and readAll. I am looking to mock these methods but I am uncertain about the process. Can ...

Encountering issues with the dependency tree while trying to install npm packages

I'm encountering an exception while attempting to install npm packages using the npm i command. The error message is displayed in this link: https://i.sstatic.net/x8N3W.png Despite trying to reinstall Node.js and turning off the proxy with these com ...

Is it possible for a typed function to access object properties recursively?

Is there an efficient method in TypeScript to type a function that can recursively access object properties? The provided example delves two levels deep: function fetchNestedProperty<T, K extends keyof T>(obj: T, prop1: K, prop2: keyof T[K]) { r ...

The insecure connection to http://bs-local.com:3000 has prevented access to geolocation

Hi everyone, I hope your day is going well. I could really use some assistance with an issue I've run into. I'm currently trying to test my React web app on BrowserStack's mobile screens, but I am doing the testing locally using localhost. ...

Angular: Issue encountered while attempting to differentiate an '[object Object]'. Arrays and iterables are the only permissible types for this operation

I encountered the following error message while attempting to retrieve updated data: Error trying to diff '[object Object]'. Only arrays and iterables are allowed Snippet of Get Code: allDatas allData(data) { this.allDatas = data } Up ...

Utilize forRoot to pass configuration data

When using Angular, I encountered a challenge in passing configuration data to a custom library. Within the user's application, they are required to provide config data to my library through the forRoot method: // Importing the custom library import ...

Is there a way to programmatically trigger a CodeAction from a VSCode extension?

Can I trigger another extension's code action programmatically from my VSCode extension? Specifically, I want to execute the resolveCodeAction of the code action provider before running it, similar to how VSCode handles Quick Fixes. Most resources su ...

Using Typescript to combine strings with the newline character

Currently, I am delving into Angular2 and facing the challenge of creating a new line for my dynamically generated string. For example: input: Hello how are you ? output: Hello how are you? Below is the code snippet: .html <div class="row"> ...