Bas van Dorst
Bas van Dorst

Reputation: 6630

Typescript: Force a deep partial assigned property to have the right type

I have created a DeepPartial type in Typescript:

type DeepPartial<T> = {
    [P in keyof T]?: DeepPartial<T[P]>;
};

And use this functionality to assign data to an object. So far so good!

class Order {
   constructor(obj: DeepPartial<Order>) {
    Object.assign(this, obj);
  }
  id: string = '';
  lines: OrderLine[] = [];

  getInfo() {
    return 'Order ID:'+this.id;
  }
}

const order = new Order({id:'982734'});
console.log(order.getInfo()); // OK (Order ID:982734")

But now I also would like to assign order lines to this object. The OrderLine itself looks like this:

class OrderLine {
   constructor(obj: DeepPartial<OrderLine>) {
    Object.assign(this, obj);
  }
  product: string = '';

  getInfo() {
    return 'Product name:'+this.product;
  }
}

The partial copy itself looks fine, because I'm able to access the products:

const order = new Order({
  id:'982734',
  lines: [
    {product:'pizza'},
    {product:'cheese'}
  ]
});
console.log(order.getInfo()); // OK (Order ID:982734")

order.lines.forEach(line => {
  console.log(line.product); // OK (pizza + cheese)
})

But when I try to access the getInfo() on the OrderLine I see the error message "(line.getInfo is not a function)"

order.lines.forEach(line => {
  console.log(line.getInfo()); // NOK (line.getInfo is not a function)
})

It looks like the issue is that order.lines is not an instance of OrderLine, but how can I fix this with a deep partial copy without casting order.lines explicitly? Example code

Upvotes: 2

Views: 595

Answers (1)

Shubham Khatri
Shubham Khatri

Reputation: 281942

The problem with your code is that you are not really converting lines object within Orders to be of type OrderLine. Updating your code as below will work

type DeepPartial<T> = {
    [P in keyof T]?: DeepPartial<T[P]>;
};

class Order {
   constructor(obj: DeepPartial<Order>) {
    Object.assign(this, obj);
    this.lines = this.lines.map(line =>  new OrderLine(line));
  }
  id: string = '';
  lines: OrderLine[] = [];

  getInfo() {
    return 'Order ID:'+this.id;
  }
}

Playground Link

Upvotes: 1

Related Questions