Skip to content

Instantly share code, notes, and snippets.

@leandroluk
Last active May 14, 2026 16:41
Show Gist options
  • Select an option

  • Save leandroluk/5ad73a2cd6c22c61ff4d62507c96d77c to your computer and use it in GitHub Desktop.

Select an option

Save leandroluk/5ad73a2cd6c22c61ff4d62507c96d77c to your computer and use it in GitHub Desktop.
A mixin to extend multiple classes while maintaining typing.
/* eslint-disable @typescript-eslint/no-explicit-any */
/**
* Utility type to convert a union of types into an intersection of types.
* Ex: UnionToIntersection<{a: 1} | {b: 2}> = {a: 1, b: 2}
*/
type UnionToIntersection<U> = (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I : never;
/**
* Generic type to represent a constructor class.
*/
type Constructor<T = {}> = abstract new (...args: any[]) => T;
/**
* Combines multiple abstract classes into a single base class.
* Copies prototype methods and static members from all classes.
*/
export function extendsAll<T extends Constructor[]>(
...bases: T
): Constructor<UnionToIntersection<InstanceType<T[number]>>> {
abstract class Mixed {}
for (const base of bases) {
// Copy instance methods
const proto = base.prototype;
for (const key of Object.getOwnPropertyNames(proto)) {
if (key === 'constructor') continue;
Object.defineProperty(Mixed.prototype, key, Object.getOwnPropertyDescriptor(proto, key)!);
}
// Copy static references
for (const key of Object.getOwnPropertyNames(base)) {
if (['length', 'prototype', 'name'].includes(key)) continue;
Object.defineProperty(Mixed, key, Object.getOwnPropertyDescriptor(base, key)!);
}
}
return Mixed as any;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment