https://github.com/samdenty/mix-classes
Seamlessly combine class inheritance with composition, guaranteed to work with any class
https://github.com/samdenty/mix-classes
Last synced: 11 months ago
JSON representation
Seamlessly combine class inheritance with composition, guaranteed to work with any class
- Host: GitHub
- URL: https://github.com/samdenty/mix-classes
- Owner: samdenty
- Created: 2019-05-04T13:05:00.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2019-10-21T22:26:50.000Z (over 6 years ago)
- Last Synced: 2024-11-26T21:15:11.391Z (over 1 year ago)
- Language: TypeScript
- Homepage:
- Size: 817 KB
- Stars: 7
- Watchers: 4
- Forks: 0
- Open Issues: 5
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# mix-classes
Easily add typescript-safe mixins to JS classes, with support for generics, constructors, overloading and more. Correctly handles `this` for each class, so it'll work with anything.
- Typescript generics
- Pass arguments to mixins, by providing an array of arguments
- Supports `super` calls in overloaded methods
- Use [`instanceof`](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Operators/instanceof) to check for mixin classes
- Handles the `this` inside classes, so that they always access their local scope first. No need to worry about name-collisions
```ts
import { Mix } from 'mix-classes'
class Contactable {
constructor(public email: string, public phone?: string) {}
}
class Nameable {
constructor(public name: string) {}
}
class Website {
constructor(public websiteUrl: string) {}
}
class Developer extends Mix(Nameable, Contactable, Website) {
constructor() {
super(['Bob'], ['hi@example.com'], ['https://example.com'])
}
}
class Company extends Mix(Nameable, Contactable) {
constructor() {
super(['Apple'], ['hi@apple.com', '18-00'], ['https://apple.com'])
}
}
const developer = new Developer()
developer.name
developer.email
developer.websiteUrl
const company = new Company()
company.name
company.email
company.phone
company.websiteUrl
```
## Constructor arguments
You can pass custom constructor arguments to each mixin within an array inside the `super` call. The arguments order is dependant on the `mix` array order.
```ts
import { Mix } from 'mix-classes'
class Nameable {
constructor(public name: string) {}
}
class Ageable {
constructor(public age: number) {}
}
class Person extends Mix(Nameable, Ageable) {
constructor() {
super(['Bob'], [50])
// ^ name argument for Nameable
// ^ age argument for Ageable
}
}
```
## Overloading
All mixins are seperate classes with different `this` values, meaning you don't need to worry about name collisions.
```ts
import { Mix, getMixin } from 'mix-classes'
class A {
variable = 'a'
public a() {
return this.variable
}
}
class B {
variable = 'b'
public b() {
return this.variable
}
}
class Test extends Mix(A, B) {
constructor() {
super()
// The default value is the last mixin specified
console.log(this.variable) // 'b'
// Use getMixin to get overloaded properties
console.log(getMixin(this, A).variable) // 'a'
// Mixins retain access to their local variables
this.a() // 'a'
this.b() // 'b'
}
}
const test = new Test()
```
## Typescript generics
Typescript generics are supported, but it requires using Typescript's declaration merging.
To use them, simply wrap the class that you want to pass generics to in `Generic()`, and then add an interface with the same name as the class you want it in.
Before:
```ts
import { Mix } from 'mix-classes'
class MyClass extends Mix(
User,
Nameable,
Ageable
) {}
```
After:
```ts
import { Generic, Mix } from 'mix-classes'
// Move all generic type signatures to the interface
// including default values.
interface MyClass extends User<'bob'> {}
class MyClass extends Mix(Generic(User), Nameable, Ageable) {}
```
```ts
import { Mix, Generic } from 'mix-classes'
class B {}
class Role {
constructor(public type: Type) {}
}
class User {
constructor(public username: Username) {}
}
interface Admin extends User<'bob'>, Role<'admin'> {}
class Admin extends Mix(Generic(User), Generic(Role), B) {
constructor() {
super(['bob'], ['admin'])
}
}
const test = new Admin()
test.username // type 'bob'
```