Inheritance can work between User and Participant classes if User is generic to specify the type of its props field.
Another tip is to rely on constants (the props values when empty) to get the type. It makes the code terser.
const emptyUserProps = {
firstname: "",
lastname: "",
email: "",
};
export type UserProps = typeof emptyUserProps;
export class User<TProps extends UserProps = UserProps> {
props = emptyUserProps as TProps; // Type assertion `as TProps` is needed here in order to initialise the field using `emptyUserProps` (of type `UserProps`, not `TProps`)
}
// -------
const emptyParticipantProps = {
pseudonym: "",
karma: 0,
impact: 0,
level: 0,
experience: 0,
};
export type ParticipantProps = typeof emptyParticipantProps;
export class Participant extends User<UserProps & ParticipantProps> {
props = {
...emptyUserProps,
...emptyParticipantProps,
};
}
But if User and Participant share only some data in props field, it's safer to achieve polymorphism using on a base interface rather than class inheritance:
export interface WithProps<TProps> {
props: TProps;
}
// ----
const emptyUserProps = {
firstname: "",
lastname: "",
email: "",
};
export type UserProps = typeof emptyUserProps;
export class User implements WithProps<UserProps> {
props = emptyUserProps;
}
// -------
const emptyParticipantProps = {
pseudonym: "",
karma: 0,
impact: 0,
level: 0,
experience: 0,
};
export type ParticipantProps = typeof emptyParticipantProps;
export class Participant implements WithProps<UserProps & ParticipantProps> {
props = {
...emptyUserProps,
...emptyParticipantProps,
};
}
Another option is to use a single generic class with several static factory methods, one to create a "basic" user, another one for a participant:
const emptyUserProps = {
firstname: "",
lastname: "",
email: "",
};
const emptyParticipantProps = {
pseudonym: "",
karma: 0,
impact: 0,
level: 0,
experience: 0,
};
export class User<TProps> {
// `TProps` inferred to `typeof emptyUserProps`
static createUser(props = emptyUserProps) {
return new User(props);
}
// `TProps` inferred to `(typeof emptyUserProps) & (typeof emptyParticipantProps)`
static createParticipant(props = {
...emptyUserProps,
...emptyParticipantProps,
}) {
return new User(props);
}
private constructor(public props: TProps) {}
}