3

I'm new to TS, and I have a question about initializing classes Here example of parsing object

const myObj = {
    "square": 100,
    "trees": [
        {
            "height": 100,
            "needles": 100500
        },
        {
            "height": 50,
            "apples": 20
        }
    ]
};


class Forest {
    constructor(
        public square: number,
        public trees: (Pine | AppleTree)[]
    ) {}
}

class Tree {
    constructor(
        public height: number,
    ) {}
}

class Pine extends Tree {
    constructor(
        public height: number,
        public needles: number,
    ) {
        super(height);
    }
}

class AppleTree extends Tree {
    constructor(
        public height: number,
        public apples: number,
    ) {
        super(height);
    }

    public getApples() {
        console.log(this.apples);
    }
}


export function initMyClass() {
    const forest: Forest = myObj;
}

But here is a compilation error - myObject Type is not assignable to type 'Forest'. How can I parse object to that class? And how to determine what subclass of Tree should be used to parse particular object in array

I tried interfaces, but, maybe not in a right way

Aluan Haddad
  • 29,886
  • 8
  • 72
  • 84
Krechmer
  • 31
  • 4

2 Answers2

0

The problem is because AppleTree is not a plain object but rather it contains a public instance method called getApples. In JavaScript, you need to instantiate a class for you to get the methods defined in the class.

As the original myObj doesn't contain the getApples, TypeScript noticed that it doesn't match the AppleTree class that you defined, which is why TypeScript complained about the error.

class Forest {
  constructor(public square: number, public trees: (Pine | AppleTree)[]) {}
}

class Tree {
  constructor(public height: number) {}
}

class Pine extends Tree {
  constructor(public height: number, public needles: number) {
    super(height);
  }
}

class AppleTree extends Tree {
  constructor(public height: number, public apples: number) {
    super(height);
  }

  public getApples() {
    console.log(this.apples);
  }
}

const myObj = {
  square: 100,
  trees: [
    {
      height: 100,
      needles: 100500
    },
    new AppleTree(50, 20)
  ]
};

export function initMyClass() {
  const forest: Forest = myObj;
}
AngYC
  • 3,051
  • 6
  • 20
  • Thanks for your answer! But what to do, if `myObj` is going from JSON.parse. I know the structure, but I don't know, what type of `Tree` is going next in array. Is there a way to init all subclass of `Tree` while parsing `myObj`? – Krechmer Apr 28 '23 at 04:01
  • Hi @Krechmer, you can consider adding a `parse` static method in all those classes, where you can call it like `Forest.parse(json)` where you can add logic to detect and instantiate those raw object – AngYC Apr 28 '23 at 04:07
0

I solved my problem using lib https://www.npmjs.com/package/class-transformer The code is below:

enum TreeTypes {
    PINE = "pine",
    APPLETREE = "appleTree",
}

abstract class Tree {
    @Expose() public height: number;
    @Expose() public type: string;
}

class Pine extends Tree {
    @Expose() public height: number;
    @Expose() public needles: number;
}

class AppleTree extends Tree {
    @Expose() public height: number;
    @Expose() public apples: number;
    
    public getApples() {
        console.log(this.apples);
    }
}

class Forest {
    @Expose() square: number;
    
    @Type(() => Tree, {
        keepDiscriminatorProperty: true,
        discriminator: {
            property: 'type',
                subTypes: [
                { value: AppleTree, name: TreeTypes.APPLETREE },
                { value: Pine, name: TreeTypes.PINE },
            ],
        },
    })
    @Expose()
    trees: (Pine | AppleTree)[];

    @Expose() 
    getSquare() {
        console.log(this.square);
    }
}



export function initMyClass() {
    const forest: Forest = plainToClass(Forest, myObj, { excludeExtraneousValues: true, enableCircularCheck: true, exposeDefaultValues: true });
    console.log(forest);
    
}
Krechmer
  • 31
  • 4