Reputation: 821
Basically, I want to be able to combine any number of classes, which, for example, will be defined in the same way as mixinHobbies
or mixinNotes
, and make TypeScript and IntelliSense not go crazy.
The order of mixins can be constant, but the number of ones that will be applied is varying.
function mixinHobbies(Base: PersonCtor, hobbies: string[]) {
return class extends Base {
hobbies = hobbies;
}
}
function mixinNotes(Base: PersonCtor, notes: string[]) {
return class extends Base {
notes = notes;
}
}
interface PersonCtor {
new (name: string, age: number): IPerson
}
interface IPerson {
name: string;
age: number;
}
class Person implements IPerson {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
const PersonWithHobbies = mixinHobbies(Person, ['drawing', 'reading']);
const PersonWithHobbiesAndNotes = mixinNotes(PersonWithHobbies, ['killlisted']);
const alex = new PersonWithHobbiesAndNotes('Alex', 18);
console.log(alex.hobbies);
console.log(alex.hobbies);
throws Property 'hobbies' does not exist on type '(Anonymous class)'
. IntelliSense also doesn't see the hobbies
field.
Tho if executed, logs fine.
Something like this would be just perfect:
function mixinPerson() {
return new class {
result = DefaultPerson;
Hobbies(hobbies: string[]) {
this.result = class extends this.result {
hobbies = hobbies;
}
return this;
}
Notes(notes: string[]) {
this.result = class extends this.result {
notes = notes;
}
return this;
}
}
}
interface PersonCtor {
new (name: string, age: number): IPerson
}
interface IPerson {
name: string;
age: number;
}
class DefaultPerson implements IPerson {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
const SuperPerson = mixinPerson()
.Hobbies(['drawing', 'reading'])
.Notes(['killlisted'])
.result;
const alex = new SuperPerson('Alex', 18);
console.log(alex.hobbies);
Upvotes: 2
Views: 1753
Reputation: 329943
I think you just need your mixins to be generic in the type of the Base
constructor, so that the compiler does not widen it to typeof Person
and forget about any additional structure:
function mixinHobbies<C extends new (...args: any) => Person>(Base: C, hobbies: string[]) {
return class extends Base {
hobbies = hobbies;
}
}
function mixinNotes<C extends new (...args: any) => Person>(Base: C, notes: string[]) {
return class extends Base {
notes = notes;
}
}
You can see that it works as desired:
const alex = new PersonWithHobbiesAndNotes('Alex', 18);
console.log(alex.hobbies); // no compiler error
Upvotes: 3