microsoft / microsoft/TypeScript-Website
A better way to use mixins ?
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 2.6k
- Forks
- 1.5k
- Avg merge
- 2d 2h
- Merged PRs (30d)
- 7
Description
The document gives some examples on how to use mixin here: https://www.typescriptlang.org/docs/handbook/mixins.html
However the 2 proposed solutions has issues:
- the first one prevent us from having private/protected members, and may generate quite a lot of type deduction error in the function.
- the second is not that great regarding to type safety.
I think we have a better solution to use mixin, that could be added to the documentation:
class Base {}
// declare the added properties outside of the mixin function
abstract class Mixin {}
class K extends mix(Base, Mixin) { }
Such generic function could be implemented like this:
const MixSrc = Symbol();
type Mix<
Base extends new(...args:any[])=>any,
Mixin extends abstract new(...args:any[])=>any,
> = Omit<Base & Mixin, "new"> & (new(...args:ConstructorParameters<Base>) => (InstanceType<Base> & InstanceType<Mixin>))
function mix<
Base extends new(...args:any[]) => any,
Mixin extends abstract new(...args:any[]) => any
>(base: Base, mixin: Mixin): Mix<Base, Mixin> {
class _ extends base {}
const static_props = Object.getOwnPropertyDescriptors(mixin);
delete static_props.prototype;
delete static_props.name;
Object.defineProperties( _, static_props );
((_ as any)[MixSrc] ??= []).push(mixin);
const hasInstance = mixin[Symbol.hasInstance];
Object.defineProperty(mixin, Symbol.hasInstance, {
value: function (instance: any) {
if( instance.constructor[MixSrc].includes(this) )
return true;
return hasInstance.call(this, instance);
},
writable: false,
});
const instance_props = Object.getOwnPropertyDescriptors(mixin.prototype);
// @ts-ignore
delete instance_props.constructor;
Object.defineProperties( _.prototype, instance_props );
return _ as any; // well...
}
There are 2 limitations of this method:
- This may not work if
Mixininherit from another class ( though you could just callmixfor each of theMixinbases ). - This will not work if
Mixinhas private#properties.
If we have private properties, we can do something like:
abstract class Mixin {
// declare the public/protected interface here
}
type ExcludeProtected<T> = {[K in keyof T]: T[K]}
function addMixin<...>(base): Mix<Base, Mixin> {
return class _ extends base implements ExcludeProtected<Mixin> {
// implements Mixin methods here...
// can't guarantee the protected interface (due to TS limitations).
} as any;
}
But then Mixin must not inherit from another class.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with the TypeScript Handbook mixins page linked in the issue and review the supplied Playground example. Assess how the proposed pattern and its stated limitations fit the existing documentation, then update the mixins guidance with an accurate alternative and clear caveats.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- documentation
- Issue type
- Documentation
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 38/100