Giter Club home page Giter Club logo

ts-mixer's People

Contributors

battosuai avatar hakimio avatar matthewwolfe avatar naranjamecanica avatar qfl1ck32 avatar tannerntannern avatar tinkerborg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

ts-mixer's Issues

6.0.3 is broken

After the update to 6.0.3 my project is broken, no errors it just does not mix up

Statics are not inherited

Hello! I just installed your module to our project and unfortunely we met an issue during implementation.

We have a lot of nested classes what have statics attributes\methods. But these methods are not accessible when the classes are loaded through your Mixin.

I have created a simple example for you.

import {Mixin} from "ts-mixer";
class TEST1 {
  static a() {
    console.log("A")
  }
}

class TEST2 extends Mixin(TEST1) {}
window.TEST2 = TEST2;

If I type TEST2.a to console. The function is undefined.

Maybe it could be related to @babel/plugin-proposal-class-properties with @babel/plugin-transform-typescript. We are not using the babel's syntax plugin but only transform.

Let me know if you need any help. But to me, seems this should be part of ts-mixer and it should work.

Thank you very much.

package.json

@babel/plugin-transform-typescript: 7.5.5,
@babel/plugin-proposal-class-properties: 7.5.5,
ts-mixer: 3.1.2,
typescript: 3.5.3,

babel.config.js

const presets = [
  [
    "@babel/env",
    {
      targets: {
        edge: "17",
        firefox: "60",
        chrome: "70",
        safari: "11.1",
      },
      useBuiltIns: "usage",
      corejs: {
        version: 3,
        proposals: true,
      },
    },
  ],
  // ["@babel/preset-typescript"], disabled because of bug with auto-assign in constructor. Instead of this is used ["@babel/plugin-transform-typescript"]
]

const plugins = [
  ["@babel/plugin-transform-typescript"],
  ["@babel/plugin-syntax-dynamic-import"],
  ["@babel/plugin-proposal-decorators", {decoratorsBeforeExport: false}],
  ["@babel/plugin-proposal-class-properties"],
  ["@babel/plugin-proposal-private-methods"],
  ["@babel/plugin-proposal-optional-chaining"],
  ["@babel/plugin-proposal-nullish-coalescing-operator"],
  ["@babel/plugin-proposal-throw-expressions"],
  [
    "@babel/plugin-transform-runtime",
    {
      corejs: {
        version: 3,
        proposals: true,
      },
      helpers: true,
      regenerator: true,
      useESModules: false,
    },
  ],
]

module.exports = {presets, plugins}

Mixin doesn't retain decorators

My use case is simple. I use TypeORM as my ORM and we're trying to do something like this.

Class Schema {
    @Column(...)
    public id: string;
}

class DefaultAble {
{
    @Column(...)
    public isDefault: boolean;
}

class SomeEntity extends Mixin(Schema, DefaultAble)
{}

The problem is the fact that I do get SomeEntity that's a real mixin of the two classes, but lose the two decorators. As a result, the two properties do not get recognized as columns in TypeORM.

I am aware that I could manually re-apply the decorators by doing some shenanigans like Column(...)(SomeEntity, "id"), but is there possibly a more seamless option?

Multi mixins when using with @nestjs/mongoose not working

Consider the following case:

import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
import { decorate, Mixin } from 'ts-mixer';

export type PostDocument = Post & Document;

@Schema()
class Likeable{
    @decorate(Prop())
    likes:number
}

@Schema()
class Tagable{
    @decorate(Prop())
    tags:string[]
}

@Schema({ timestamps: true })
class Post extends Mixin(Tagable,Likeable){
    @Prop()
    title:string;
    @Prop()
    body:string;
}

export const PostSchema = SchemaFactory.createForClass(Post);

Now using mongoose to update the document will ignore the Mixin props

    // get the post document then do
    post.likes = 10;
    post.save();

the document will not change in the database. but if you used only one mixin the above example will work.

// This will work fine (Note only used one mixin)
@Schema({ timestamps: true })
class Post extends Mixin(Likeable){
    @Prop()
    title:string;
    @Prop()
    body:string;
}

Getters / Setters not working anymore

Hi,
I've upgraded to v5 and in my project (typescript -> babel -> js) a getter and setter method was not called any more in a class which was using a mixin.

Right now i've downgraded, next week I have more time to dive into the manner...

thanks πŸ‘

Check if object's class has mixin

Is there a way to check if an object uses a certain mixin (its class is defined with that mixin)? My use case for example would be

        this.map.entities().forEach((entity) => {
            if (hasMixin(entity, Actor)) {
                const actor = entity as unknown as Actor;
                // use as Actor
            }
        });

I could work around this by checking if the object has a certain function, but I would prefer a more general solution (since this solution would be specific to the methods of each mixin)

How does it work under the hood?

How amazing...🎩🎩🎩🎩🎩

I've tried figured out how this thing works, soon I realized, this is too hard to understand for me.

Could you add some explanation for this on the README ?? Thanks!

TypeError: Cannot call a class as a function (only in production build)

Using tx-mixer 4.0.0 with react-scripts 3.3.0 + typescript 3.7.2, I'm getting "TypeError: Cannot call a class as a function" inside the constructor of a mixin-using class, but only in the production build. The development build works. I saw #2 but it seems that affected all builds and was resolved. Any ideas?

class is wok, but if run with jest will throw error

import LRU from '../';

describe('clear() sets the cache to its initial state', () => {

	console.dir(LRU)

});
  ● Test suite failed to run

    TypeError: Object prototype may only be an Object or null: undefined
        at Function.create (<anonymous>)



      at Object.exports.hardMixProtos (node_modules/ts-mixer/dist/util.js:54:31)
      at Object.Mixin (node_modules/ts-mixer/dist/mixins.js:16:18)

Only 10 classes are allowed for mixins.

Hey, I'm using your library for a project that needs 12/13 mixin classes (I know weird) but apparently this library only handles 10? I read bit of the code and saw that the function is declared manually. Is there a possibility to add as much as I need in terms of classes or is that a limit ?

Mixing using `decorator` doesn't work right

Here is the code:

class MixinA {
  foo = 'a';
  method() {
    console.log('method from A');
  }
}

class MixinB {
  foo = 'b';
  method() {
    console.log('method from B');
  }
}

@mix(MixinA, MixinB)
class MixedWithDecorator {
  foo = 'mixed';
  method() {
    console.log('method from Mixed');
  }
}

class MixedWithExtends extends Mixin(MixinA, MixinB) {
  foo = 'mixed';
  method() {
    console.log('method from Mixed');
  }
}

console.log('Decorated:');
const decorated = new MixedWithDecorator();
console.log(decorated.foo);
decorated.method();

console.log('\nExtended:');
const extended = new MixedWithExtends();
console.log(extended.foo);
extended.method();

The output is:

Decorated:
b
method from B
 
Extended:
mixed
method from Mixed

That's because Mixin(BaseClass, ...ingredients) is not the same as class BaseClass extends Mixin(...ingredients).

Is it possible to get proper `this` inside a decorator for a method of a class extending Mixin(...)?

Hi, @tannerntannern!

Thank you for this awesome library, it really helped me and my colleagues πŸš€

But I have a small issue. I'm a bit confused on decorating methods in the class extending a mixin (or multiple mixins).

Suppose I have a base class (A) with two methods: methodA and methodB which use A's properties internally.
I have another class B that extends A and has a decorated method. What I want to do is to call A.methodA and A.methodB inside the decorator. But I'm getting the following error error "Cannot read properties of undefined" inside the A.methodA call for some reason.

Is there a way to get the proper this (B instance with every property from A) inside the decorator?

Minimal example to reproduce the issue:

import { BehaviorSubject, Observable, of } from 'rxjs';
import { finalize } from 'rxjs/operators';
import { Mixin } from 'ts-mixer';

// I want to decorate methods that return an Observable
type ObservableMethod = (...args: unknown[]) => Observable<unknown>;
type ObservableMethodDecorator = (
  target: any,
  propertyKey: string | symbol,
  descriptor: TypedPropertyDescriptor<ObservableMethod>
) => TypedPropertyDescriptor<ObservableMethod> | void;

function LockUiUntilFinish(): ObservableMethodDecorator {
  return function (
    target: any,
    propertyKey: string,
    descriptor: TypedPropertyDescriptor<ObservableMethod>
  ): TypedPropertyDescriptor<ObservableMethod> {
    const originalFn = descriptor.value;

    descriptor.value = (...args: unknown[]): Observable<unknown> => {
      target?.lockUi?.(); // <------------ at this point target does not have the `isUiLocked$` property

      return originalFn(...args).pipe(finalize(() => target?.unlockUi?.()));
    };

    return descriptor;
  };
}

class Base {
  protected isUiLocked = false;
  public readonly isLoading$ = new BehaviorSubject<boolean>(true);

  public readonly isUiLocked$ = new BehaviorSubject<boolean>(this.isUiLocked);

  public lockUi(): void {
    console.log('lockUi');
    this.isUiLocked = true;

    this.isUiLocked$.next(this.isUiLocked);
  }

  public unlockUi(): void {
    console.log('unlockUi');
    this.isUiLocked = false;

    this.isUiLocked$.next(this.isUiLocked);
  }
}

class Foo extends Mixin(Base) {
  constructor() {
    super();
  }

  @LockUiUntilFinish()
  public test(): Observable<unknown> {
    return of(100);
  }
}

const foo = new Foo();

// I would like to get the following output:
// > lockUi
// > 100
// > unlockUi
foo.test().subscribe(console.log);

mixin-based multiple inheritance not working with nestjs/swagger decorators

Hi there,

Let me try to describe the issue with ts-mixer I'm facing right now. So I'm using nestjs as the main dev framework for the backend and currently implementing the register method that would allow the application to register all types of users. There're a few possible types of users here. Some of them are FAN, FIGHTER, JUDGE etc. Each user has its own set of specific parameters. For example, FIGHTER has such params as height, weight, wins etc that are specific to fighters only.

So far the register method looks like this:

    @Post('register')
    public async registerUser(@Body() registerUserDto: RegisterUserDto): Promise<void> {
        // code
    }

That RegisterUserDto is supposed to contain all the fields coming from all types of users, but frontend-wise, when I register a specific user I should be allowed to specify only those json fields relevant to the type of user I'm registering. It means that the majority of fields defined on RegisterUserDto will be optional. The thing is that, in order to improve code readability and code maintenance, I would like to have separate dtos for each possible type of user and then pile them all together into a single object (dto) using multiple inheritance or the intersection type. The second option does not work cos in that case the class-validator and nestjs/swagger libs stop seeing their decorators. The first option seems to be sort of doable thanks to the ts-mixer library, cos as it's stated in its documentation we should wrap validation decorators in the @decortor decorator. It works like a charm, but...it's not the case with the nestjs/swagger decorators and that's the problem I'm trying to solve. For instance, when I use @ApiProperty decorator on any of the defined props I get this error:

(node:22654) UnhandledPromiseRejectionWarning: Error: A circular dependency has been detected (property key: "firstName"). Please, make sure that each side of a bidirectional relationships are using lazy resolvers ("type: () => ClassType").
    at SchemaObjectFactory.createNotBuiltInTypeReference (/Users/albert/Documents/projects/albert/rlx/node_modules/@nestjs/swagger/dist/services/schema-object-factory.js:212:19)
    at SchemaObjectFactory.mergePropertyWithMetadata (/Users/albert/Documents/projects/albert/rlx/node_modules/@nestjs/swagger/dist/services/schema-object-factory.js:143:25)
    at /Users/albert/Documents/projects/albert/rlx/node_modules/@nestjs/swagger/dist/services/schema-object-factory.js:79:35
    at Array.map (<anonymous>)
    at SchemaObjectFactory.extractPropertiesFromType (/Users/albert/Documents/projects/albert/rlx/node_modules/@nestjs/swagger/dist/services/schema-object-factory.js:78:52)
    at SchemaObjectFactory.exploreModelSchema (/Users/albert/Documents/projects/albert/rlx/node_modules/@nestjs/swagger/dist/services/schema-object-factory.js:92:41)
    at /Users/albert/Documents/projects/albert/rlx/node_modules/@nestjs/swagger/dist/services/schema-object-factory.js:33:36
    at Array.map (<anonymous>)
    at SchemaObjectFactory.createFromModel (/Users/albert/Documents/projects/albert/rlx/node_modules/@nestjs/swagger/dist/services/schema-object-factory.js:20:45)
    at exploreApiParametersMetadata (/Users/albert/Documents/projects/albert/rlx/node_modules/@nestjs/swagger/dist/explorers/api-parameters.explorer.js:33:55)
(Use `node --trace-warnings ...` to show where the warning was created)
(node:22654) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). To terminate the node process on unhandled promise rejection, use the CLI flag `--unhandled-rejections=strict` (see https://nodejs.org/api/cli.html#cli_unhandled_rejections_mode). (rejection id: 1)
(node:22654) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Here is more code:

export class RegisterCommonUserDto {
    @decorate(ApiProperty())
    @decorate(IsNotEmpty())
    @decorate(IsString())
    firstName: string;

    // @decorate(ApiProperty())
    @decorate(IsNotEmpty())
    @decorate(IsString())
    lastName: string;

    // @decorate(ApiProperty({enum: EUserRoleName}))
    @decorate(IsNotEmpty())
    @decorate(IsEnum(EUserRoleName))
    roleName: EUserRoleName;

    // @decorate(ApiProperty())
    @decorate(IsNotEmpty())
    @decorate(IsPhoneNumber())
    phoneNumber: string;

    // @decorate(ApiProperty())
    @decorate(IsNotEmpty())
    @decorate(IsEmail())
    email: string;
}

and the RegisterUserDto looks as follows:

export class RegisterUserDto extends Mixin(
    RegisterFighterDto,
    RegisterCommonUserDto,
    RegisterLocationProviderDto,
) {}

I would appreciate your help!

Forgot to add `is-class` dependency?

Webpack throws Module not found: Error: Can't resolve 'is-class' in '/project/node_modules/ts-mixer/dist' during compilation process.

dist/mixins.js contains var isClass = require("is-class"); on line 3, but the dependencies list is empty in package.json.

Doesn't pick up validation decorators for class-validator

compared to the mixin function provided by TypeScript.

CODE

import { IsBoolean, IsIn, validate } from "class-validator";
import { Mixin } from 'ts-mixer'

function applyMixins(derivedCtor: any, baseCtors: any[]) {
  baseCtors.forEach(baseCtor => {
      Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
          Object.defineProperty(derivedCtor.prototype, name, Object.getOwnPropertyDescriptor(baseCtor.prototype, name));
      });
  });
}

// Disposable Mixin
class Disposable {
  @IsBoolean()
  isDisposed: boolean = false;
}

// Activatable Mixin
class Statusable {
  @IsIn(['bound', 'open'])
  status: string = 'test';
}

class ExtendedObject extends Mixin(Disposable, Statusable) {

}

class MixedObject {
  constructor() { 
    this.status = 'test'
    this.isDisposed = false
  }
}
interface MixedObject extends Disposable, Statusable {}
applyMixins(MixedObject, [Disposable, Statusable]);

async function test(classObj)
{
  let smartObj = new classObj();

  console.log(smartObj)
  console.log(smartObj.status)
  console.log(smartObj.isDisposed)
  
  console.log(await validate(smartObj))
}

async function main() {
  await test(MixedObject)
  console.log('-----------')
  await test(ExtendedObject)
}

main()

OUTPUT

Statusable { status: 'test', isDisposed: false }
[
  ValidationError {
    target: Statusable { status: 'test', isDisposed: false },
    value: 'test',
    property: 'status',
    children: [],
    constraints: { isIn: 'status must be one of the following values: bound,open' }
  }
]
-----------
ExtendedObject { isDisposed: false, status: 'test' }
[]

TypeError: WEBPACK_IMPORTED_MODULE_0__.CrudApi is not a constructor

If I instantiate a mixin in another ts file I get a error.

Here is the code in a file (it works).

export abstract class BaseApi {

    protected readonly apiUrlPart: string;

    protected constructor( apiUrlPart: string ) {
        this.apiUrlPart = apiUrlPart;
    }
}

export class ApiCreate<T extends Entity> extends BaseApi {

    public constructor( apiUrlPart: string ) {
        super( apiUrlPart );
    }

    public create( entity: T ): Observable<T> {
        return null;
    }
}

export class ApiQuery<T extends Entity> extends BaseApi {

    public constructor( apiUrlPart: string ) {
        super( apiUrlPart );
    }

    public query( id: string | number ): Observable<T> {
        return null;
    }
}

export class ApiQueryAll<T extends Entity> extends BaseApi {

    public constructor( apiUrlPart: string ) {
        super( apiUrlPart );
    }

    public queryAll( entity: T ): Observable<T> {
        return null;
    }
}

export class ApiUpdate<T extends Entity> extends BaseApi {

    public constructor( apiUrlPart: string ) {
        super( apiUrlPart );
    }

    public update( entity: T ): Observable<T> {
        return null;
    }
}

export class ApiDelete<T extends Entity> extends BaseApi {

    public constructor( apiUrlPart: string ) {
        super( apiUrlPart );
    }

    public delete( entity: T ): Observable<T> {
        return null;
    }
}

@MixinDecorator( ApiCreate, ApiQuery, ApiQueryAll, ApiUpdate, ApiDelete )
export class CrudApi<T extends Entity> extends BaseApi {
    public constructor( apiUrlPart: string ) {
        super( apiUrlPart );
    }
}

export interface CrudApi<T extends Entity> extends ApiCreate<T>, ApiQuery<T>, ApiQueryAll<T>, ApiUpdate<T>, ApiDelete<T> {}

const a: CrudApi<Rocket> = new CrudApi<Rocket>( 'rocket' ); // <-- here it works

But that does not work:
File api.ts

// tslint:disable variable-name max-classes-per-file
import { CrudApi } from './mixins.ts';
import { Rocket} from './rocket';

export class RocketApi {
    public readonly Rocket: CrudApi<Rocket> = new CrudApi<Rocket>( 'rocket' );
}

Error:

main.ts:12 TypeError: WEBPACK_IMPORTED_MODULE_0__.CrudApi is not a constructor
    at ...
    at _createClass (core.js:21165)
    at _createProviderInstance (core.js:21137)
    at initNgModule (core.js:21070)
    at new NgModuleRef_ (core.js:21797)
    at createNgModuleRef (core.js:21786)
    at Object.debugCreateNgModuleRef [as createNgModuleRef] (core.js:23617)
    at NgModuleFactory_.push../node_modules/@angular/core/fesm5/core.js.NgModuleFactory_.create (core.js:24321)
    at core.js:17755

static attributes are not inherited from derived class

Hello again!

I have a class what inherits another class. Where the another class has static attributes but line 163 doesn't take in account inner prototypes.

Case:

class TEST0 {
  static scope = 10;
}
class TEST1 extends TEST0 {}
class TEST2 extends Mixin(TEST1) {}
window.TEST2 = TEST2

scope is undefined at TEST2.

Here is an example of constructor to describe the issue:
Class2 constructor contains:

{
  "tagName",
  "prototype": Class1
}

Class1 Constructor:

{
  "scope": 10
}

But prototype is restricted from getOwnPropertyNames and scope will be skipped.

mixin mode can't work with decorator

Hi, @tannerntannern .

Thanks for your fantastic job! I'm happy with that repo!

however, i found there's some issue that ts-mixer doesn't work. the error message is:

MissingReflectMetadata: Could not reflect metadata of type design:type, did you forget to enable "emitDecoratorMetadata" on compiler options?

even I have opened this switch.

i rebuild a repo here which can reproduce this error : https://github.com/flame4/ts-mixer-demo.

Actually i just learned ts for 2 months so i don't know too much underlayer details, please take a look on this repo thanks!

Mix decorator is not copying the base class name

First of all, I would like to congratz you for your useful package.

But I have a suggestion or maybe a bug fix, depending on whether it's a design or not xD
I have been using the mix decorator in some classes for about 1 month and it's a bit confusing having all of them with the same name (MixedClass) as it's showed in the picture.
Just copying the property constructor.name in the class returned by the decorator would solve this issue.

MixedClass issue

Best regards,

Memory leak in mixin tracking

The fact that mixins are being tracked globally in a map leads to a memory leak as the mixins are never unregistered from the map, preventing garbage collection of the respective classes.

const mixins = new Map<any, Function[]>();

Simple solution would be to use a WeakMap.

Doesnt work in IE11

Ts-mixer doesnt work in IE11. IE11 doesnt support ES6 syntax. Any workaround or fix?

Decorator deep inheritance not working

I'm working with class validator and ts-mixer, but it seems to have a problem inheriting inherited decorators. It inherits the decorators from the parent, but not from grandparents.

Example:

class Disposable {
    @decorate(IsBoolean()) // instead of @IsBoolean()
    isDisposed: boolean = false;
}

class Statusable {
    @decorate(IsIn(['red', 'green'])) // instead of @IsIn(['red', 'green'])
    status: string = 'green';
}

class Statusable2 {
    @decorate(IsIn(['red', 'green'])) // instead of @IsIn(['red', 'green'])
    other: string = 'green';
}

class ExtendedObject extends Mixin(Disposable, Statusable) {}

class ExtendedObject2 extends Mixin(Statusable2, ExtendedObject) {}

const extendedObject = new ExtendedObject2();
extendedObject.status = 'blue';
extendedObject.other = 'blue';
extendedObject.isDisposed = undefined;

validate(extendedObject).then(errors => {
    console.log(errors);
});

This causes only validations from Statusable2 to be triggered, and the ones from ExtendedObject to be ignored.

A workaround is to do Mixin(Statusable2, Disposable, Statusable, ExtendedObject), but the best solution for maintainability.

Sometimes constructor does not see elements from default init function.

  • Sometimes constructor does not see elements from default init function.
class Class0 {
  public name;
}

abstract class ClassA {
  protected object0: Class0;
  protected name;
  protected test;

  init(test: string, object0?: Class0): void {
    this.name = this.constructor.name;
    this.test = test;
    this.object0 = object0 ? object0 : new Class0();
  }

  getName(): string {
    return this.name;
  }

  getTest(): string {
    return this.name;
  }
}

class ClassB extends ClassA {
  protected name1;
  init(test: string, object0?: Class0): void {
    super.init(test, object0);
    this.name1 = this.name + 1;
  }

  getName1(): string {
    return this.name1;
  }
}

const b = new ClassB('BB'); // Not possible

Using custom interface in abstract extended class

Hi,
first of all thank you for this amazing library.

I was wondering if would be possible to extend an abstract class (along with some others) that needs a T.
For example:

export abstract class MyAbstractClass<T> { ... }

and then in the main component :

export class MyComponent extends Mixin(MyAbstractClass<string>, foo, bar, ...) { ... }

Maybe I'm missing something but I can't figure how to do it.

The error given is:

Value of type 'typeof MyAbstractClass' is not callable. Did you mean to include 'new'?

how to initiate the property of super classes

I have code like
import { Mixin } from 'ts-mixer';

class Foo {
public a: string;
protected makeFoo() {
return 'foo';
}
public constructor(a:string)
{
this.a = a
}
}

class Bar {
public b: string;
public c: string;
public constructor(b:string,c:string)
{
this.b = b
this.c = c;
}
protected makeBar() {
return 'bar';
}
}

class FooBar extends Mixin(Foo, Bar) {
public constructor()
{
super("a")
}
public makeFooBar() {
return this.makeFoo() + this.makeBar();
}
}

const fooBar = new FooBar();

how can I set value to property "a" of super class Foo and properties b and c of super class Bar in constructor of FooBar?

if I use super("value") then, both a and b will have value "value"
what if I want to assign different value to a, b and c in constructor of FooBar?

TypeError when running tests in Angular v15

Hello. Thanks for making a great library.
I usually use this library with Angular, but when I set Angular to v15, it compiles fine with ng serve, but with ng test I get the following error.

TypeError: Cannot use 'in' operator to search for 'ngOnChanges' in undefined

The Angular component class under test is as follows.

class Foo {
  echoFoo(): void {
    console.log('foo');
  }
}

class Bar {
  echoBar(): void {
    console.log('bar');
  }
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss']
})
export class AppComponent extends Mixin(Foo, Bar) implements OnInit {
  title = 'check-ts-mixer-angular15';

  ngOnInit(): void {
    this.echoFoo();
    this.echoBar();
  }
}

The error occurs when looking for the Angular lifecycle hook method from the prototype of the parent class of the Angular component, but this error does not occur when only one class is specified as the argument of Mixin, and this error seems to occur when two or more are specified.

I apologize if this is the wrong place to ask this, but is there any solution to this problem?

Here is the configuration when I tested locally.

Node.js : 14.20.0 or 18.12.1
npm : 6.14.17 or 8.19.2
Angular : 15.0.1
ts-mixer : 6.0.2

I have created a github repository with a dependency on this library added to a minimal Angular project created with ng cli, so I hope this helps.

https://github.com/gkasse/check-ts-mixer-angular15

Also, the sample repository uses karma as a test runner, but I have the same problem when testing with jest in another project.

Thank you in advance.

Property 'makeFoo' does not exist on type 'FooBar'.

I'm trying to run an example from your repository, but I'm getting an error.
I also tried to copy my ts-config into the repl.it sandbox and there is no such problem. Do you have any idea why i am getting this error
On my computer, the version of ts is 4.4.4 (the same version is in the sandbox)

Please help me figure out what could be the problem?
If you need more details please let me know.
My tsconfig.json

{
  "compilerOptions": {
    "baseUrl": "src",
    "skipLibCheck": true,
    "module": "commonjs",
    "esModuleInterop": true,
    "noImplicitReturns": false,
    "noUnusedLocals": true,
    "outDir": "lib",
    "sourceMap": true,
    "strict": true,
    "target": "es2017",
    "noImplicitAny": true,
    "lib": ["es2019", "es2020", "dom"],
    "types": ["chai", "mocha", "node"],
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "resolveJsonModule": true
  },
  "compileOnSave": true,
  "include": ["src"],
  "exclude": ["node_modules", "**/*.test.ts", "**/*.spec.ts"]
}

image

hasMixin doesn't work with abstract classes

I am trying to test for the presence of an abstract mixin class and am getting a ts2345 error:

Argument of type 'typeof HasMixinTest' is not assignable to parameter of type 'new (...args: any[]) => HasMixinTest'.
  Cannot assign an abstract constructor type to a non-abstract constructor type.ts(2345)

Example code to reproduce this issue:

import { hasMixin, Mixin } from 'ts-mixer'

abstract class HasMixinTest {}

abstract class Foo extends Mixin(HasMixinTest) {}

hasMixin(Foo, HasMixinTest)

Argument of type 'Function' is not assignable to parameter of type 'PropertyDecorator | MethodDecorator | ClassDecorator'.

Getting this with TypeORM decorator:

import { Entity, CreateDateColumn } from 'typeorm'
import { decorate } from 'ts-mixer'

@Entity()
export class CreatedAt {
  @decorate(CreateDateColumn())
  createdAt!: Date
}
(alias) CreateDateColumn(options?: ColumnOptions | undefined): Function
import CreateDateColumn
This column will store a creation date of the inserted object. Creation date is generated and inserted only once, at the first time when you create an object, the value is inserted into the table, and is never touched again.

Argument of type 'Function' is not assignable to parameter of type 'PropertyDecorator | MethodDecorator | ClassDecorator'.
  Type 'Function' is not assignable to type 'ClassDecorator'.
    Type 'Function' provides no match for the signature '<TFunction extends Function>(target: TFunction): void | TFunction'.ts(2345)

Screen-Shot-2020-04-20-13-58-49

Any ideas?

Thank you!

Abstract mixins

I'd like to use abstract classes for mixins to impose requirement on the classes that use them, like this:

import { Mixin } from 'ts-mixer'

class Parent {
}

abstract class TestMixin {
  public get bar() {
    return this.foo
  }

  protected abstract get foo(): string
}

class Test extends Mixin(Parent, TestMixin) {
  protected get foo(): string {
    return 'foo'
  }
}

This produces an error on the Mixin line:

Cannot assign an abstract constructor type to a non-abstract constructor type. TS2345

Could something like this be possible?

Uncaught TypeError: Illegal invocation

If you try to inherit from a native Element such as HTMLElement, then you get an Uncaught TypeError: Illegal invocation.

import { Mixin } from 'ts-mixer';
import { BaseController } from './../controllers/BaseController';

export class BaseComponent extends Mixin(HTMLElement, BaseController) {
    constructor() {
        super();
        ...

I think this can become the best solution for mixins. If you look for https://www.npmjs.com/package/ts-mixins you will see that there is the issue, that the first instance of my Controller is an instance of BaseComponent and later its an instance of a native HTMLElement. As a result, the constructor acts on the wrong object. (But if u use this package you must change HTMLElement and BaseController)

But this only by the way... Please think of it ;)

Try your awesome Mixins with Webcomponents... This is very important for at least me =)

Getting an error when using with Jest

When I try to run a test on a class I've applied a mixin to I get this error:
TypeError: Cannot redefine property: Symbol(Symbol.hasInstance)
at Function.defineProperties ()

Any thoughts on how I can get around this?

Better API for multiple decorators (very low priority, since the API design is elegant enough 😁)

First of all, thanks for your precious time and efforts πŸ™πŸ™πŸ™

Extend #15 (comment)

Status quo:

import 'reflect-metadata'
import { Mixin, decorate } from 'ts-mixer'
import { IsInt, IsNotEmpty, IsUrl, IsEmail, Min, Max } from 'class-validator'

class User {
  @decorate(IsInt())
  id!: number
  
  @decorate(IsNotEmpty())
  name!: string
}

class Profile {
  // Looks a bit verbose and repetitive 😒
  @decorate(IsInt())
  @decorate(Min(0))
  @decorate(Max(120))
  age!: number

  @decorate(IsUrl())
  avatar!: string
}

class UserProfile extends Mixin(User, Profile) {
  @IsEmail()
  email!: string
}

Proposal:

import 'reflect-metadata'
import { Mixin, decorate } from 'ts-mixer'
import { IsInt, IsNotEmpty, IsUrl, IsEmail, Min, Max } from 'class-validator'

class User {
  @decorate(IsInt())
  id!: number
  
  @decorate(IsNotEmpty())
  name!: string
}

class Profile {
  // Looks better πŸ˜‚
  @decorate(
    IsInt(),
    Min(0),
    Max(120)
  )
  age!: number

  @decorate(IsUrl())
  avatar!: string
}

class UserProfile extends Mixin(User, Profile) {
  @IsEmail()
  email!: string
}

Ts-mixer with Angular

Hello,

Thank you for this nice library, it seems to be very interesting to use for our use cases.

However, I'm not able to use it properly in Angular. Here is an example :

// Global config for ts-mixer :

import { settings } from 'ts-mixer';

settings.prototypeStrategy = 'proxy';
settings.staticsStrategy = 'proxy';

export default settings;
// Component receiving all extending classes
@Component({
  selector: 'one-forms-input-text',
  templateUrl: './text.component.html',
  styles: [],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: TextComponent,
      multi: true,
    },
  ],
})
export class TextComponent extends Mixin(
  LengthValidatorTemplateMixin,
  ControlValueAccessorConnector
) {
  /**
   * Used to implement a CSS class directly on the host
   * @internal
   * @protected
   */
  @decorate(HostBinding('class.one-forms-input-text'))
  protected override hostClass = true;

  /**
   * Suffix icon to display
   */
  @decorate(Input())
  icon: MaterialIcon = '';

  constructor(injector: Injector) {
    super(injector);
  }
}
export class ControlValueAccessorConnector implements ControlValueAccessor {
  /** Default class applied on Host element */
  @decorate(HostBinding('class.one-forms-input'))
  protected hostClass = true;

  /** Update user-select CSS property */
  @decorate(HostBinding('style.user-select'))
  protected userSelectMode = 'auto';
  /**
   * Used to implement the display CSS property directly on the host
   *
   * @protected
   */
  @decorate(HostBinding('style.display')) protected hostDisplay =
    'inline-block';
  /**
   * @internal
   *
   * used internally to manage the form control
   */
  @decorate(ViewChild(FormControlDirective, { static: true }))
  formControlDirective: FormControlDirective | undefined;

  /**
   * First way to set the formControl by using the formControl itself
   */
  @decorate(Input())
  formControl: FormControl | undefined;

  /**
   * Another way to set the formControl by using the formControlName
   */
  @decorate(Input())
  formControlName = '';

  /**
   * Placeholder of the input, when there is no user input
   */
  @decorate(Input())
  placeholder = '';

  /**
   * Label to name the input
   */
  @decorate(Input())
  label = '';

  /**
   * Add without-label class to the host component if no label specified
   * @protected
   */
  @decorate(HostBinding('class.without-label'))
  protected get hostWithoutLabel() {
    return this.label === '';
  }

  /**
   * The message to describe the input by setting the mat-hint
   */
  @decorate(Input())
  description = '';

  constructor(private injector: Injector) {}
export class LengthValidatorTemplateMixin {
  /**
   * Minimum length for the input
   */
  @decorate(Input()) minLength: string | number | null = null;

  /**
   * Maximum length for the input
   */
  @decorate(Input()) maxLength: string | number | null = null;
}

The error thrown is:
Error: Cannot set new properties on Proxies created by ts-mixer

After some investigation, it seems there is a problem with the constructor:
2022-06-21_15h49_28

Do you have any idea how I could use ts-mixer in Angular properly?

Thank you and have a nice day

Caveat 3 is not true

Caveat 3 states there is no way to call the constructor of a class with the correct this context. This is in fact not true.

As can be seen in the examples at https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect/construct a simple statement change can achieve just that.

// Replace
new constructor(...args);
// With
Reflect.construct(constructor, args, this);

Am I missing something here?

I believe I see where this change would need to be made, but I'm not 100% sure so I am making this issue first.

@mix multiple classes and passing constructor args

Firstly, this lib is neat - thanks for making it!

One thing I'm struggling with (I don't know if it's even possible or how it would work) is how to mix two classes that require constructor args. Here's an example:

import { mix } from 'ts-mixer';
import { Class1, Class2 } from './example';

export interface IMixed<T1, T2> extends Class1<T1>, Class2<T2> {

}

@mix(Class1, Class2)
class Mixed<T1, T2> implements IMixed<T1, T2> {
  constructor(args) {
    super(args); // How would you pass args to the correct class instance here?
  }
}

Is there a different pattern I could follow to get a similar result? Any help would be appreciated!

reflect-metadata is lost when mixing in

I'm trying to use this with classes which have metadata added via reflect-metadata.

Previously I was using the old TS mixin style (https://www.typescriptlang.org/docs/handbook/mixins.html) but all of my classes are generic and the hoops that I need to jump through are extreme!

I think the typings you have done a re really nice and it's a decent way of achieving a similar result.

I have a lot of code which relies on reflect-metadata and also on generic mixins. In hindsight I wish I hadn't written it but it's too late now! It also has quite a few places where it's relying on instanceof as well.

I have forked your code and made some modifications for my use case, I added an Augment(Base, Extras) function which will subclass the Base class and mixin the Extras class. This way reflect-metadata and instanceof both work for the Base class (but not the Extras of course).

I also added a @base decorator which works similarly to @mix but it subclasses the first argument passed to it.

I will send you a PR but I will not be offended if you don't want to add support for these use cases.

Type 'className' is missing the following properties from type 'IWorkerItem': init, postProcess, needProcess

I managed to set up your library, but for some reason I get an error that some methods are not described in the classes (they should not be described, since I expect that they will be used from the parent class)

error TS2739: Type 'NinjaOneDeviceWorkerItem' is missing the following properties from type 'IWorkerItem': init, postProcess, needProcess
image

Here is the class that Mixin uses (it extends NinjaOneWorkerItem and DeviceWorkerItem)
Π‘Π½ΠΈΠΌΠΎΠΊ экрана 2023-01-18 Π² 11 44 50

Here is NinjaOneWorkerItem which extends WorkerItem (parent)
image

Here is DeviceWorkerItem which also extends WorkerItem (parent)
image

And here is parent WorkerItem (which contain default init, postProcess, needProcess)
image

and interface for WorkerItem
image

I have no idea what it could be related to.

Support getters/setters in mixin classes

mixedClassProto[prop] = protoChain[i][prop];

This line doesn't properly defines a property on target prototype if it's a getter/setter.
Probably this code can be used instead but I didn't test it:

const props = Object.getOwnPropertyDescriptors(protoChain[i]);
delete props.constructor;
Object.defineProperties(mixedClassProto, props);

Can it work with Tsyringe Decorators?

The tsyringe package - a popular dependency injection framework - uses class (e.g. @injectable) and constructor parameter (e.g. @inject) decorators. Can ts-mixer work with these decorators? I've tried and I'm getting errors.

image
image

[QUESTION or BUG] TypeError: (0 , _1.<insert function here>) is not a function

Hi, I've just discovered your library (and it seems to be awesome) but I'm encountering a weird error:

TypeError: (0 , _1.withC) is not a function

While executing the following:

import { Mixin } from 'ts-mixer';

const withC = (T: B | D) => class extends (T instanceof B ? B : D) {
  // some code here that should inherit B or D classes
}

class A extends Mixin(B, withC(new B())) {
  //                     ^
  //                TypeError: (0 , _1.withC) is not a function
}

Is there anything related to your library? Or I presume I'm doing something wrong, but I'm not experienced enough to see what I do wrong aha :/

Mixin Parent Class does not receive Child Class's "this".

When using regular heritance:

class Parent{
  constructor() {
    console.log('Parent name:'+ this.constructor.name)
  }
}

class Child extends Parent{
  constructor() {
    super()
    console.log('Child name:'+ this.constructor.name)
  }
}

let child = new Child();

Outputs:

Parent name:Child
Child name:Child

But when I use Mixin from ts-mixer:

import { Mixin } from 'ts-mixer';
class Parent {
  constructor() {
    console.log('Parent name:' + this.constructor.name);
  }
}

class Child extends Mixin(Parent) {
  constructor() {
    super();
    console.log('Child name:' + this.constructor.name);
  }
}

const child = new Child();

Outputs:

Parent name:Parent
Child name:Child

So it's not getting the correct "this"

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.