I have an interface with an extensive list of properties, each with different types and structures:
interface OrderInterface {
id: number;
orderNumber: string;
createdAt: string;
updatedAt: string;
customer: {
id: number;
firstName: string;
lastName: string;
email: string;
address: {
street: string;
city: string;
state: string;
zip: string;
country: string;
}
};
products: {
id: number;
name: string;
qty: number;
isVirtual: boolean;
}[];
// ... assume we add an arbitrary number of fields.
}
Initially, the interface satisfied my needs for simply passing data around and ensuring the structure is the same in all parts used and to satisfy TypeScript's checking. Now, I find myself needing to perform some functions on the data which would be easy if methods were tied to the data directly. I start to write the class, and I realize that my first inclination is that I have to copy and assign every single property in the constructor.
class Order implements OrderInterface {
id: number;
orderNumber: string;
createdAt: string;
updatedAt: string;
customer: {
id: number;
firstName: string;
lastName: string;
email: string;
address: {
street: string;
city: string;
state: string;
zip: string;
country: string;
}
};
products: {
id: number;
name: string;
qty: number;
isVirtual: boolean;
}[];
// Assume the arbitrary fields defined in interface are defined here too
constructor(data: OrderInterface) {
this.id = data.id;
this.orderNumber = data.orderNumber;
this.createdAt = data.createdAt;
// ...
// And I have to duplicate every property? What if a property changes? Is there a better way?
}
get customerName(): string {
return `${this.customer.firstName} ${this.customer.lastName}` || '(name not available';
}
get physicalProducts(): Product[] {
// filter products by isVirtual property and return result.
}
// ... assume more functions to get, compare, change data, etc.
}
interface Product {
id: number;
name: string;
qty: number;
isVirtual: boolean;
}
In plain JS, I think this would work in the constructor. Essentially, it would just copy all the fields from my interface to the object.
constructor(data: OrderInterface) {
Object.assign(this, data);
}
But with TypeScript, I get an error that resembles this (changed from my real code)
Error: src/app/shared/inquiry.service.ts:378:3 - error TS2564: Property 'id' has no initializer and is not definitely assigned in the constructor.
Error: src/app/shared/inquiry.service.ts:379:3 - error TS2564: Property 'orderNumber' has no initializer and is not definitely assigned in the constructor.
Error: src/app/shared/inquiry.service.ts:380:3 - error TS2564: Property 'createdAt' has no initializer and is not definitely assigned in the constructor.
Error: src/app/shared/inquiry.service.ts:381:3 - error TS2564: Property 'updatedAt' has no initializer and is not definitely assigned in the constructor.
Error: src/app/shared/inquiry.service.ts:382:3 - error TS2564: Property 'customer' has no initializer and is not definitely assigned in the constructor.
Error: src/app/shared/inquiry.service.ts:395:3 - error TS2564: Property 'products' has no initializer and is not definitely assigned in the constructor.
// And continued Error for arbitrary property having no initializer nor being assigned in constructor
Even though technically all the correct fields should be converted to the new class instantiation, TypeScript doesn't seem to recognize this happening. What is an elegant way to do this in the constructor?
Or, is trying to convert this interface to a class "not the Angular way" to begin with? Is it more "Angular" to keep the data as an interface and put any functions dealing with model(s) in service classes?