There are two different ways to approach this:
- Adhere strictly to the encapsulation of mixins. Mixins should stand alone and not have knowledge about where they will be mixed in. This means that a mixin should not assume it has been added to a class with an
id
field:
// Following this first approach maintains strict encapsulation of mixins
export abstract class ConfigMixin {
// Your mixin will require all necessary information as parameters
// Hence, whenever you call saveConfig, you must pass the id along
public saveConfig(id: string) {
console.log(id);
}
}
// The base class
class BaseItem {
constructor(public id: string) {}
}
// Implementing a subclass of BaseItem
export class BaseDevice extends BaseItem {}
// Specifying that BaseDevice includes ConfigMixin
export interface BaseDevice extends ConfigMixin {}
// Applying the ConfigMixin
applyMixins(BaseDevice, [ConfigMixin]);
Link to TypeScript playground
- Sidestep TypeScript's rules! This method results in code that is more aligned with your original strategy but does come with a downside:
// Utilizing an abstract class field characterizes the second option
export abstract class ConfigMixin {
public abstract id: string;
public saveConfig() {
console.log(this.id);
}
}
// The base class
class BaseItem {
constructor(public id: string) {}
}
// Creating a subclass of BaseItem
export class BaseDevice extends BaseItem {}
// Indicating that BaseDevice has ConfigMixin integrated
export interface BaseDevice extends ConfigMixin {}
// NEGATIVE ASPECT
//
// Unfortunately, using this technique, TypeScript will not raise errors when
// attempting to include ConfigMixin in a class lacking the id field
export class SomeDevice {}
export interface SomeDevice extends ConfigMixin {}
// Finally, applying the ConfigMixin
applyMixins(BaseDevice, [ConfigMixin]);
applyMixins(SomeDevice, [ConfigMixin]);
Link to TypeScript playground