0

I am trying to create and use some Data Classes in NodeJs which i defined in Typescript and are at a point where i am wondering if there is a simpler way.

In javascript i was able to do

let myBuilding = new Building

Then i was able to just do

myBuilding.col1 = "Wall"
myBuilding.col2 = "None"

and so on

in typescript it doesn't like it if i don't declare everything at the point of declaration. Is there a way to initialize a class with blank values and then assign them later ? Also what happens when there is something that doesnt get a value assigned ? in javascript we dont get that item returned which is great when parsing from json to a class

Here is what a class of mine looks like

export class Exterior {
    public exterior: string;
    public fencing: string;
    public security: string;
    public sewer: string;
    public lot: string;
    public pool: string;
    public patioPorch: string;
    public spa: string;

    constructor(exterior: string, fencing: string, security: string, sewer: string, lot: string, pool: string,
                patioPorch: string, spa: string) {

        this.exterior = exterior;
        this.fencing = fencing;
        this.security = security;
        this.sewer = sewer;
        this.lot = lot;
        this.pool = pool;
        this.patioPorch = patioPorch;
        this.spa = spa;
    }
}
MisterniceGuy
  • 1,646
  • 2
  • 18
  • 41
  • One of the big reasons to use TypeScript is that by declaring the shape of each object up front, you can be more confident that each object is being used correctly elsewhere. If `col1` is *supposed* to be a `string`, wouldn't you want `myBuilding.col1 = -1` or to generate a compile-time error? If `col1` could be `null`, wouldn't you want the compiler to warn you about unsafe usage like `myBuilding.col1.length`? Show us how you've declared the class right now and how you're parsing the JSON. Explain exactly what you'd like to change about the current solution. There could be another way. – p.s.w.g Apr 02 '19 at 22:38
  • I guess it has its advantages and disadvantages. But it makes it a huge mess if i have to assign all values when i create the new instance of class specially if i have like 50 fields and a bunch of arrays. – MisterniceGuy Apr 02 '19 at 22:47
  • You don't necessarily have to assign each property by hand. It depends on *how safe* you really want to be. If you completely trust the JSON you're passing in, you could just have `constructor(json: string) { Object.assign(this, JSON.parse(json)); }` or something similar (like I said, I'd have to see exactly how your class is defined and how you're parsing in order to provide a complete answer). – p.s.w.g Apr 02 '19 at 23:07
  • Please accept an answer if you got your answer and if not, ask us so we can help you. – makeitmorehuman Apr 15 '19 at 17:47

2 Answers2

0

Here are four ways of achieving this:

class Foo {
    // This will not do anything, so remove it
    constructor() {}

    // this will be undefined initially
    private internalA!: number;

    public get fieldA() {
        return this.internalA
    }

    public set fieldA(internalAValue: number) {
        this.internalA = internalAValue;
    }

    // this will be undefined initially
    public fieldB!: boolean;
    // this will be undefined initially
    public fieldC!: string;
    // this will be "example-initial-value" initially
    public fieldD: string = "example-initial-value";
}

const foo = new Foo();

// Method 1 using setters
foo.fieldA = 2;
alert(foo.fieldA);

// Method 2 using simple assigning
foo.fieldB = true;
alert(foo.fieldB);

// Method 3 using Object.defineProperty
Object.defineProperty(foo, 'fieldC', {
  value: "test",
  writable: false
});
alert(foo.fieldC);

// Method 4 using Object.assign
Object.assign(foo, {fieldD: "hello"});
alert(foo.fieldD);

Be very careful or even avoid Object.defineProperty and Object.assign directly without creating a wrapper that enforces the types. They both have many ways of getting around / forgetting your type system.

setter method and direct public field assignment are the easiest type safe ways.

You can run it here

Here is a way for setting multiple things in one go without initialising first

interface IFooParams {
    fieldA: number;
    fieldB: boolean;
    fieldC: string;
    fieldD: string
}

class Foo {
    // this will be undefined initially
    public fieldA!: number;
    // this will be undefined initially
    public fieldB!: boolean;
    // this will be undefined initially
    public fieldC!: string;
    // this will be "example-initial-value" initially
    public fieldD: string = "example-initial-value";

    public setAllInOneGo(params: IFooParams): void {
        this.fieldA = params.fieldA;
        this.fieldB = params.fieldB;
        this.fieldC = params.fieldC;
        this.fieldD = params.fieldD;
    }
}

const foo = new Foo();

// Whenever:
foo.setAllInOneGo({
    fieldA: 2,
    fieldB: false,
    fieldC: "hello",
    fieldD: "world"
});
makeitmorehuman
  • 11,287
  • 3
  • 52
  • 76
  • So first off , what does the ! after for example fieldB! ? Also this only works if i dont have a constructor in my class, otherwise Typescript complains that it is expecting X Arguments – MisterniceGuy Apr 03 '19 at 02:14
  • It means I declare that take responsibility for this field having the initial value of undefined / I accept not initialising it's value. – makeitmorehuman Apr 03 '19 at 02:15
  • More on the bang operator here: https://stackoverflow.com/questions/42273853/in-typescript-what-is-the-exclamation-mark-bang-operator-when-dereferenci – makeitmorehuman Apr 03 '19 at 02:21
  • Based on typescript docs if i dont create a constructor in my class, typescript will automatically create on . A class may contain at most one constructor declaration. If a class contains no constructor declaration, an automatic constructor is provided, Whats strange is that even so my class might look exact the same is the automatically created one they dont act the same – MisterniceGuy Apr 03 '19 at 02:22
  • If you add an empty constructor() {} to my example, it will behave exactly the same way – makeitmorehuman Apr 03 '19 at 02:26
  • Did you find the answer useful or conclusive for your question? – makeitmorehuman Apr 03 '19 at 08:43
  • To be honest it seems that there are more then one way to get this done. Based on my code sample which posted above, it also works if i change the constructor to exterior?: string and so on. – MisterniceGuy Apr 03 '19 at 16:23
  • Your sample code assigns values in the constructor which means things get assigned immediately after the class is instantiated. But in your question you stated that you need to initialise the class with blank values and then assign them later, which is the reason I omitted the use of the constructor method. – makeitmorehuman Apr 03 '19 at 17:02
0

when you declare a type you can just make things optional:

class Building {
  height?: number;
}

now typescript won't complain if you don't declare a height right away but you still can't add extra undeclared fields like width.

You can also declare things as a Partial<Building> if they meet some subset of the interface but not all of the required fields.

bryan60
  • 28,215
  • 4
  • 48
  • 65