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

3
  • Why use classes at all when none of them have any methods? Commented Apr 28, 2023 at 13:09
  • @Bergi Of cause they do. I deleted it just for this example Commented Apr 29, 2023 at 4:08
  • But they matter. myObj does not have these methods, and that's exactly what TypeScript is complaining about. Commented Apr 29, 2023 at 13:06

2 Answers 2

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;
}
Sign up to request clarification or add additional context in comments.

2 Comments

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?
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
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);
    
}

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.