Reputation: 6338
I'm creating a parent and child class in Typescript. Each one defines some fields (properties). I'm trying to keep the code clean, using the Partial<T>
method for the constructors. But I'm not sure how to pass the parent's properties to the parent in the super()
call and initialize the child's props with only the child's unique props in Object.assign()
-- if that's even the right paradigm. Here's what I have:
export class Parent {
name: string = ''
durationInSec: number = 0.0
optionalField?: string;
constructor(init: Partial<Parent>) {
Object.assign(this, init)
}
}
export enum Types { A, B, C }
export class ChildA extends Parent {
kind = Types.A
childRequired1: string = '' // ??? have to set defaults for all props with this technique
childOption1?: string
constructor(init: Partial<ChildA>) {
super(init) // ??? wrong, passes too many things to super
Object.assign(this, init) // this would be fine alone, but tsc says need to call super()
}
}
// similar for ChildB, ChildC etc.
let obj1 = new ChildA({name: 'foo', childRequired1: 'bar'})
Is this even a good paradigm in Typescript? Is there a better way?
Upvotes: 0
Views: 496
Reputation: 40653
The problem is that init
is a Partial<ChildA>
so if you don't declare a default for childRequired1
then there's nothing preventing someone from doing new ChildA({})
in fact Parent.durationInSec is actually not optional on parent but you are relying on the default being there.
The second part of the problem is that it appears that using Object.assign(this, ...)
in the constructor does not seem to satisfy the requirement of initialising all required parameters in the constructor. There seems to be an open issue about this.
The way I see it you have two options:
Put defaults in all the required types to ensure they always have a value and Partial
can still be used
Be much more verbose:
export class Parent {
name: string;
durationInSec: number;
optionalField?: string;
constructor(init: Parent) {
this.name = init.name;
this.durationInSec = init.durationInSec;
this.optionalField = init.optionalField;
}
}
export enum Types { A, B, C }
export class ChildA extends Parent {
kind: Types;
childRequired1: string;
childOption1?: string;
constructor(init: ChildA) {
super(init as Parent) // Downcasted, it should be ok.
this.kind = init.kind;
this.childRequired1 = init.childRequired1;
this.childOption1 = init.childOption1;
}
}
let obj1 = new ChildA({
name: 'foo',
childRequired1: 'bar',
durationInSec: 1,
kind: Types.A
});
Upvotes: 1