├── .npmrc ├── .gitattributes ├── .prettierignore ├── src ├── index.ts ├── types.ts ├── getInheritedPropertyNames.ts ├── instanceOf.ts ├── getInheritedDescriptor.ts ├── tests │ ├── Counter.js │ ├── Counter2.js │ ├── extending-builtins.test.js │ ├── extending-native-classes.test.js │ ├── friends.test.js │ ├── wrap-custom-classes.test.js │ ├── empty-classes.test.js │ ├── readme-examples.test.js │ ├── custom-elements.test.js │ ├── class-branding.test.js │ ├── configuration.test.js │ └── syntaxes.test.js ├── native.test.ts ├── Constructor.ts ├── WIP-test-lowclass-types.ts.off ├── Mixin.ts ├── utils.ts ├── Mixin.test.ts ├── native.ts └── multiple.ts ├── dist ├── types.js ├── index.js ├── index.d.ts ├── types.d.ts ├── types.js.map ├── index.d.ts.map ├── index.js.map ├── tests │ ├── Counter2.d.ts.map │ ├── Counter2.d.ts │ ├── Counter.d.ts.map │ ├── Counter.js.map │ ├── Counter2.js.map │ ├── Counter.js │ ├── Counter2.js │ └── Counter.d.ts ├── types.d.ts.map ├── getInheritedPropertyNames.d.ts ├── getInheritedPropertyNames.d.ts.map ├── instanceOf.d.ts.map ├── native.d.ts ├── getInheritedDescriptor.d.ts.map ├── instanceOf.js.map ├── Constructor.js.map ├── instanceOf.d.ts ├── getInheritedDescriptor.d.ts ├── native.d.ts.map ├── instanceOf.js ├── getInheritedDescriptor.js.map ├── getInheritedPropertyNames.js.map ├── getInheritedPropertyNames.js ├── getInheritedDescriptor.js ├── Mixin.d.ts.map ├── Mixin.d.ts ├── utils.d.ts.map ├── Constructor.d.ts.map ├── Constructor.js ├── utils.d.ts ├── multiple.d.ts.map ├── multiple.d.ts ├── Constructor.d.ts ├── Mixin.js.map ├── WIP-test-lowclass-types.ts.off ├── utils.js.map ├── Class.d.ts ├── Mixin.js ├── Class.d.ts.map ├── utils.js ├── native.js.map ├── multiple.js.map ├── native.js └── multiple.js ├── .prettierrc.cjs ├── tsconfig.json ├── .gitignore ├── .editorconfig ├── .npmignore ├── index.html ├── LICENSE ├── .github └── workflows │ └── tests.yml └── package.json /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | dist/ 3 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export const version = '8.0.2' 2 | -------------------------------------------------------------------------------- /dist/types.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | //# sourceMappingURL=types.js.map -------------------------------------------------------------------------------- /.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = require('@lume/cli/.prettierrc.js') 2 | -------------------------------------------------------------------------------- /dist/index.js: -------------------------------------------------------------------------------- 1 | export const version = '8.0.2'; 2 | //# sourceMappingURL=index.js.map -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./node_modules/@lume/cli/config/ts.config.json" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | package-lock.json 3 | .vscode/ 4 | *.log 5 | dist/**/*.test.* 6 | -------------------------------------------------------------------------------- /dist/index.d.ts: -------------------------------------------------------------------------------- 1 | export declare const version = "8.0.2"; 2 | //# sourceMappingURL=index.d.ts.map -------------------------------------------------------------------------------- /dist/types.d.ts: -------------------------------------------------------------------------------- 1 | export type Id = {} & { 2 | [P in keyof T]: T[P]; 3 | }; 4 | //# sourceMappingURL=types.d.ts.map -------------------------------------------------------------------------------- /dist/types.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /dist/index.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,OAAO,UAAU,CAAA"} -------------------------------------------------------------------------------- /dist/index.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,OAAO,GAAG,OAAO,CAAA"} -------------------------------------------------------------------------------- /dist/tests/Counter2.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Counter2.d.ts","sourceRoot":"","sources":["../../src/tests/Counter2.js"],"names":[],"mappings":"AAWA,6BAkBC;AAGD;;;;;;;EAaC"} -------------------------------------------------------------------------------- /dist/types.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAGA,MAAM,MAAM,EAAE,CAAC,CAAC,IAAI,EAAE,GAAG;KAAE,CAAC,IAAI,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;CAAC,CAAA"} -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | // `Id` is an identity type, but it is also used as a trick to expand the 2 | // type given to it so that tooltips show the basic type rather then all the 3 | // conditional/aliases used. 4 | export type Id = {} & {[P in keyof T]: T[P]} 5 | -------------------------------------------------------------------------------- /dist/getInheritedPropertyNames.d.ts: -------------------------------------------------------------------------------- 1 | /** Like Object.getOwnPropertyNames, but gets property names including from ancestor prototypes. */ 2 | export declare function getInheritedPropertyNames(obj: T): (keyof T)[]; 3 | //# sourceMappingURL=getInheritedPropertyNames.d.ts.map -------------------------------------------------------------------------------- /dist/getInheritedPropertyNames.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"getInheritedPropertyNames.d.ts","sourceRoot":"","sources":["../src/getInheritedPropertyNames.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,wBAAgB,yBAAyB,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,CAa/E"} -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | [*] 7 | charset = utf-8 8 | indent_style = space 9 | indent_size = 4 10 | end_of_line = lf 11 | insert_final_newline = true 12 | trim_trailing_whitespace = true 13 | -------------------------------------------------------------------------------- /dist/tests/Counter2.d.ts: -------------------------------------------------------------------------------- 1 | export const Counter2: never; 2 | export const Incrementor2: (new (counter?: any) => { 3 | constructor: (counter: any) => void; 4 | increment: () => void; 5 | }) & { 6 | __: { 7 | protected: {}; 8 | }; 9 | }; 10 | //# sourceMappingURL=Counter2.d.ts.map -------------------------------------------------------------------------------- /dist/tests/Counter.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Counter.d.ts","sourceRoot":"","sources":["../../src/tests/Counter.js"],"names":[],"mappings":"AAOA;;;0BAkCggC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAfjgC;AAGF;;;0BAYggC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAHhgC"} -------------------------------------------------------------------------------- /dist/instanceOf.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"instanceOf.d.ts","sourceRoot":"","sources":["../src/instanceOf.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,kBAAkB,CAAA;AAEjD;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,UAAU,CAAC,CAAC,EACnC,QAAQ,EAAE,GAAG,EACb,WAAW,EAAE,WAAW,CAAC,CAAC,CAAC,GACzB,QAAQ,IAAI,YAAY,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAI1C"} -------------------------------------------------------------------------------- /dist/native.d.ts: -------------------------------------------------------------------------------- 1 | import { Constructor } from './Constructor.js'; 2 | export { newless as native }; 3 | export default newless; 4 | type FuncLikeCtor = { 5 | (): T; 6 | new (): T; 7 | } & S; 8 | declare function newless(constructor: T): FuncLikeCtor, T>; 9 | //# sourceMappingURL=native.d.ts.map -------------------------------------------------------------------------------- /dist/getInheritedDescriptor.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"getInheritedDescriptor.d.ts","sourceRoot":"","sources":["../src/getInheritedDescriptor.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,GAAG,mBAAmB,GAAG,SAAS,CAgB9G;AAED,MAAM,WAAW,mBAAoB,SAAQ,kBAAkB;IAC9D,KAAK,EAAE,MAAM,CAAA;CACb"} -------------------------------------------------------------------------------- /dist/instanceOf.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"instanceOf.js","sourceRoot":"","sources":["../src/instanceOf.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,OAAO,UAAU,UAAU,CACjC,QAAa,EACb,WAA2B;IAE3B,IAAI,OAAO,WAAW,IAAI,UAAU,IAAI,WAAW,CAAC,MAAM,CAAC,WAAW,CAAC;QACtE,OAAO,WAAW,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,QAAQ,CAAC,CAAA;;QAC5C,OAAO,QAAQ,YAAY,WAAW,CAAA;AAC5C,CAAC"} -------------------------------------------------------------------------------- /dist/Constructor.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Constructor.js","sourceRoot":"","sources":["../src/Constructor.ts"],"names":[],"mappings":"AAWA;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAA0B,IAAyB;IAC7E,OAAO,IAA0C,CAAA;AAClD,CAAC;AAcD;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CAClC,IAAyB;IAEzB,OAAO,IAAkD,CAAA;AAC1D,CAAC;AAkBD;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAA0B,IAAyB;IAChF,OAAO,IAA6C,CAAA;AACrD,CAAC"} -------------------------------------------------------------------------------- /dist/instanceOf.d.ts: -------------------------------------------------------------------------------- 1 | import type { Constructor } from './Constructor.js'; 2 | /** 3 | * A ponyfill for `instanceof` with support for Symbol.hasInstance for older 4 | * environments. Use in place of native `instanceof`. 5 | */ 6 | export default function instanceOf(instance: any, Constructor: Constructor): instance is InstanceType>; 7 | //# sourceMappingURL=instanceOf.d.ts.map -------------------------------------------------------------------------------- /dist/getInheritedDescriptor.d.ts: -------------------------------------------------------------------------------- 1 | /** Like Object.getOwnPropertyDescriptor, but looks up the prototype chain for the descriptor. */ 2 | export declare function getInheritedDescriptor(obj: T, key: keyof T): DescriptorWithOwner | undefined; 3 | export interface DescriptorWithOwner extends PropertyDescriptor { 4 | owner: object; 5 | } 6 | //# sourceMappingURL=getInheritedDescriptor.d.ts.map -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Specificty of the following rules matters. 2 | 3 | # Ignore everything, 4 | /**/* 5 | 6 | # but include these folders 7 | !/dist/**/* 8 | !/src/**/* 9 | 10 | # except for these files in the above folders. 11 | /dist/**/*.test.* 12 | /dist/tests/**/* 13 | /src/**/*.test.* 14 | /src/tests/**/* 15 | 16 | # The following won't work as you think it would. 17 | # /**/* 18 | # !/dist/**/* 19 | # !/src/**/* 20 | # /**/*.test.* 21 | -------------------------------------------------------------------------------- /dist/native.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"native.d.ts","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAC,WAAW,EAAC,MAAM,kBAAkB,CAAA;AAE5C,OAAO,EAAC,OAAO,IAAI,MAAM,EAAC,CAAA;AAE1B,eAAe,OAAO,CAAA;AA8ItB,KAAK,YAAY,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,IAAI;IAC9B,IAAI,CAAC,CAAA;IACL,QAAQ,CAAC,CAAA;CACT,GAAG,CAAC,CAAA;AAEL,iBAAS,OAAO,CAAC,CAAC,SAAS,WAAW,EAAE,WAAW,EAAE,CAAC,GAAG,YAAY,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAwHxF"} -------------------------------------------------------------------------------- /dist/instanceOf.js: -------------------------------------------------------------------------------- 1 | /** 2 | * A ponyfill for `instanceof` with support for Symbol.hasInstance for older 3 | * environments. Use in place of native `instanceof`. 4 | */ 5 | export default function instanceOf(instance, Constructor) { 6 | if (typeof Constructor == 'function' && Constructor[Symbol.hasInstance]) 7 | return Constructor[Symbol.hasInstance](instance); 8 | else 9 | return instance instanceof Constructor; 10 | } 11 | //# sourceMappingURL=instanceOf.js.map -------------------------------------------------------------------------------- /dist/getInheritedDescriptor.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"getInheritedDescriptor.js","sourceRoot":"","sources":["../src/getInheritedDescriptor.ts"],"names":[],"mappings":"AAAA,iGAAiG;AACjG,MAAM,UAAU,sBAAsB,CAAmB,GAAM,EAAE,GAAY;IAC5E,IAAI,YAAY,GAAG,GAAG,CAAA;IACtB,IAAI,UAAU,CAAA;IAEd,OAAO,YAAY,EAAE,CAAC;QACrB,UAAU,GAAG,MAAM,CAAC,wBAAwB,CAAC,YAAY,EAAE,GAAG,CAAC,CAAA;QAE/D,IAAI,UAAU,EAAE,CAAC;YAChB,CAAC;YAAC,UAAkC,CAAC,KAAK,GAAG,YAAY,CAAA;YACzD,OAAO,UAAiC,CAAA;QACzC,CAAC;QAED,YAAY,GAAI,YAAoB,CAAC,SAAS,CAAA;IAC/C,CAAC;IAED,OAAO,KAAK,CAAC,CAAA;AACd,CAAC"} -------------------------------------------------------------------------------- /dist/getInheritedPropertyNames.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"getInheritedPropertyNames.js","sourceRoot":"","sources":["../src/getInheritedPropertyNames.ts"],"names":[],"mappings":"AAAA,mGAAmG;AACnG,MAAM,UAAU,yBAAyB,CAAmB,GAAM;IACjE,IAAI,YAAY,GAAG,GAAG,CAAA;IACtB,IAAI,IAAI,GAAgB,EAAE,CAAA;IAE1B,OAAO,YAAY,EAAE,CAAC;QACrB,IAAI,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,mBAAmB,CAAC,YAAY,CAAgB,CAAC,CAAA;QAC3E,YAAY,GAAI,YAAoB,CAAC,SAAS,CAAA;IAC/C,CAAC;IAED,oBAAoB;IACpB,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAA;IAEhC,OAAO,IAAI,CAAA;AACZ,CAAC"} -------------------------------------------------------------------------------- /src/getInheritedPropertyNames.ts: -------------------------------------------------------------------------------- 1 | /** Like Object.getOwnPropertyNames, but gets property names including from ancestor prototypes. */ 2 | export function getInheritedPropertyNames(obj: T): (keyof T)[] { 3 | let currentProto = obj 4 | let keys: (keyof T)[] = [] 5 | 6 | while (currentProto) { 7 | keys = keys.concat(Object.getOwnPropertyNames(currentProto) as (keyof T)[]) 8 | currentProto = (currentProto as any).__proto__ 9 | } 10 | 11 | // remove duplicates 12 | keys = Array.from(new Set(keys)) 13 | 14 | return keys 15 | } 16 | -------------------------------------------------------------------------------- /dist/getInheritedPropertyNames.js: -------------------------------------------------------------------------------- 1 | /** Like Object.getOwnPropertyNames, but gets property names including from ancestor prototypes. */ 2 | export function getInheritedPropertyNames(obj) { 3 | let currentProto = obj; 4 | let keys = []; 5 | while (currentProto) { 6 | keys = keys.concat(Object.getOwnPropertyNames(currentProto)); 7 | currentProto = currentProto.__proto__; 8 | } 9 | // remove duplicates 10 | keys = Array.from(new Set(keys)); 11 | return keys; 12 | } 13 | //# sourceMappingURL=getInheritedPropertyNames.js.map -------------------------------------------------------------------------------- /src/instanceOf.ts: -------------------------------------------------------------------------------- 1 | import type {Constructor} from './Constructor.js' 2 | 3 | /** 4 | * A ponyfill for `instanceof` with support for Symbol.hasInstance for older 5 | * environments. Use in place of native `instanceof`. 6 | */ 7 | export default function instanceOf( 8 | instance: any, 9 | Constructor: Constructor, 10 | ): instance is InstanceType> { 11 | if (typeof Constructor == 'function' && Constructor[Symbol.hasInstance]) 12 | return Constructor[Symbol.hasInstance](instance) 13 | else return instance instanceof Constructor 14 | } 15 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | 13 | 14 | 19 | 20 | -------------------------------------------------------------------------------- /dist/getInheritedDescriptor.js: -------------------------------------------------------------------------------- 1 | /** Like Object.getOwnPropertyDescriptor, but looks up the prototype chain for the descriptor. */ 2 | export function getInheritedDescriptor(obj, key) { 3 | let currentProto = obj; 4 | let descriptor; 5 | while (currentProto) { 6 | descriptor = Object.getOwnPropertyDescriptor(currentProto, key); 7 | if (descriptor) { 8 | ; 9 | descriptor.owner = currentProto; 10 | return descriptor; 11 | } 12 | currentProto = currentProto.__proto__; 13 | } 14 | return void 0; 15 | } 16 | //# sourceMappingURL=getInheritedDescriptor.js.map -------------------------------------------------------------------------------- /src/getInheritedDescriptor.ts: -------------------------------------------------------------------------------- 1 | /** Like Object.getOwnPropertyDescriptor, but looks up the prototype chain for the descriptor. */ 2 | export function getInheritedDescriptor(obj: T, key: keyof T): DescriptorWithOwner | undefined { 3 | let currentProto = obj 4 | let descriptor 5 | 6 | while (currentProto) { 7 | descriptor = Object.getOwnPropertyDescriptor(currentProto, key) 8 | 9 | if (descriptor) { 10 | ;(descriptor as DescriptorWithOwner).owner = currentProto 11 | return descriptor as DescriptorWithOwner 12 | } 13 | 14 | currentProto = (currentProto as any).__proto__ 15 | } 16 | 17 | return void 0 18 | } 19 | 20 | export interface DescriptorWithOwner extends PropertyDescriptor { 21 | owner: object 22 | } 23 | -------------------------------------------------------------------------------- /dist/tests/Counter.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Counter.js","sourceRoot":"","sources":["../../src/tests/Counter.js"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,qDAAqD;AAErD,OAAO,EAAC,KAAK,EAAC,MAAM,aAAa,CAAA;AAEjC,IAAI,gBAAgB,CAAA;AAEpB,MAAM,OAAO,GAAG,KAAK,CAAC,CAAC,EAAC,OAAO,EAAE,SAAS,EAAC,EAAE,EAAE;IAC9C,yDAAyD;IACzD,gBAAgB,GAAG,SAAS,CAAA;IAE5B,OAAO;QACN,KAAK;YACJ,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAA;QAC3B,CAAC;QAED,OAAO,EAAE;YACR,KAAK,EAAE,CAAC;SACR;QAED,SAAS,EAAE;YACV,SAAS;gBACR,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAA;YACtB,CAAC;SACD;KACD,CAAA;AACF,CAAC,CAAC,CAAA;AAEF,oDAAoD;AACpD,MAAM,WAAW,GAAG,KAAK,CAAC,CAAC,EAAC,OAAO,EAAC,EAAE,EAAE,CAAC,CAAC;IACzC,WAAW,CAAC,OAAO;QAClB,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,GAAG,OAAO,CAAA;IAChC,CAAC;IAED,SAAS;QACR,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAA;QACrC,gBAAgB,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,CAAA;IACtC,CAAC;CACD,CAAC,CAAC,CAAA;AAEH,OAAO,EAAC,OAAO,EAAE,WAAW,EAAC,CAAA"} -------------------------------------------------------------------------------- /dist/tests/Counter2.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Counter2.js","sourceRoot":"","sources":["../../src/tests/Counter2.js"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,mDAAmD;AAEnD,OAAO,EAAC,KAAK,EAAC,MAAM,aAAa,CAAA;AAEjC,4DAA4D;AAC5D,EAAE;AACF,8EAA8E;AAC9E,uCAAuC;AACvC,IAAI,SAAS,GAAG,EAAE,CAAA;AAElB,MAAM,QAAQ,GAAG,KAAK,CACrB,UAAU,EACV,CAAC,EAAC,OAAO,EAAC,EAAE,EAAE,CAAC,CAAC;IACf,KAAK;QACJ,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,CAAA;IAC3B,CAAC;IAED,OAAO,EAAE;QACR,KAAK,EAAE,CAAC;KACR;IAED,SAAS,EAAE;QACV,SAAS;YACR,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,CAAA;QACtB,CAAC;KACD;CACD,CAAC,EACF,SAAS,CACT,CAAA;AAED,sDAAsD;AACtD,MAAM,YAAY,GAAG,KAAK,CACzB,cAAc,EACd,CAAC,EAAC,OAAO,EAAE,SAAS,EAAC,EAAE,EAAE,CAAC,CAAC;IAC1B,WAAW,CAAC,OAAO;QAClB,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,GAAG,OAAO,CAAA;IAChC,CAAC;IAED,SAAS;QACR,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,OAAO,CAAA;QACrC,SAAS,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,CAAA;IAC/B,CAAC;CACD,CAAC,EACF,SAAS,CACT,CAAA;AAED,OAAO,EAAC,QAAQ,EAAE,YAAY,EAAC,CAAA"} -------------------------------------------------------------------------------- /src/tests/Counter.js: -------------------------------------------------------------------------------- 1 | // show how to do something similar to "friend" in C++ or "package protected" 2 | // in Java, using intentionally leaked access helpers 3 | 4 | import {Class} from '../Class.js' 5 | 6 | let CounterProtected 7 | 8 | const Counter = Class(({Private, Protected}) => { 9 | // leak the Counter class Protected helper to outer scope 10 | CounterProtected = Protected 11 | 12 | return { 13 | value() { 14 | return Private(this).count 15 | }, 16 | 17 | private: { 18 | count: 0, 19 | }, 20 | 21 | protected: { 22 | increment() { 23 | Private(this).count++ 24 | }, 25 | }, 26 | } 27 | }) 28 | 29 | // note how Incrementor does not extend from Counter 30 | const Incrementor = Class(({Private}) => ({ 31 | constructor(counter) { 32 | Private(this).counter = counter 33 | }, 34 | 35 | increment() { 36 | const counter = Private(this).counter 37 | CounterProtected(counter).increment() 38 | }, 39 | })) 40 | 41 | export {Counter, Incrementor} 42 | -------------------------------------------------------------------------------- /dist/Mixin.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Mixin.d.ts","sourceRoot":"","sources":["../src/Mixin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,kBAAkB,CAAA;AAGjD,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,SAAS,WAAW,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,CAAC,KAAK,CAAC,CAAA;AAC3E,MAAM,MAAM,wBAAwB,GAAG,CAAC,CAAC,SAAS,WAAW,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC,EAAE,CAAC,KAAK,CAAC,CAAA;AAGvF,MAAM,MAAM,WAAW,CAAC,MAAM,SAAS,WAAW,EAAE,KAAK,SAAS,WAAW,IACzE,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC,CAAC,GAAG,MAAM,GAAG,KAAK,CAAA;AAE5E,wBAAgB,KAAK,CAAC,CAAC,SAAS,aAAa,EAAE,OAAO,EAAE,CAAC,EAAE,WAAW,CAAC,EAAE,WAAW,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG;IAAC,KAAK,EAAE,CAAC,CAAA;CAAC,CAchH;AAED,eAAe,KAAK,CAAA;AACpB,OAAO,EAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAC,CAAA;AAG/D,iBAAS,WAAW,CAAC,CAAC,SAAS,aAAa,EAAE,YAAY,EAAE,CAAC,EAAE,OAAO,EAAE,WAAW,iBAMlF;AAED,iBAAS,MAAM,CAAC,CAAC,SAAS,aAAa,EAAE,YAAY,EAAE,CAAC,UAIhB,WAAW,SASlD;AAED,iBAAS,WAAW,CAAC,CAAC,SAAS,aAAa,EAAE,YAAY,EAAE,CAAC,iBAkD5D;AAGD,iBAAS,YAAY,CAAC,CAAC,SAAS,aAAa,EAAE,YAAY,EAAE,CAAC,KAI7D;AAGD,iBAAS,MAAM,CAAC,CAAC,SAAS,aAAa,EAAE,YAAY,EAAE,CAAC,iBAWvD"} -------------------------------------------------------------------------------- /dist/tests/Counter.js: -------------------------------------------------------------------------------- 1 | // show how to do something similar to "friend" in C++ or "package protected" 2 | // in Java, using intentionally leaked access helpers 3 | import { Class } from '../Class.js'; 4 | let CounterProtected; 5 | const Counter = Class(({ Private, Protected }) => { 6 | // leak the Counter class Protected helper to outer scope 7 | CounterProtected = Protected; 8 | return { 9 | value() { 10 | return Private(this).count; 11 | }, 12 | private: { 13 | count: 0, 14 | }, 15 | protected: { 16 | increment() { 17 | Private(this).count++; 18 | }, 19 | }, 20 | }; 21 | }); 22 | // note how Incrementor does not extend from Counter 23 | const Incrementor = Class(({ Private }) => ({ 24 | constructor(counter) { 25 | Private(this).counter = counter; 26 | }, 27 | increment() { 28 | const counter = Private(this).counter; 29 | CounterProtected(counter).increment(); 30 | }, 31 | })); 32 | export { Counter, Incrementor }; 33 | //# sourceMappingURL=Counter.js.map -------------------------------------------------------------------------------- /dist/Mixin.d.ts: -------------------------------------------------------------------------------- 1 | import type { Constructor } from './Constructor.js'; 2 | export type MixinFunction = >(BaseClass: T) => T; 3 | export type MixinFunctionWithDefault = >(BaseClass?: T) => T; 4 | export type MixinResult = Constructor & InstanceType> & TClass & TBase; 5 | export declare function Mixin(mixinFn: T, DefaultBase?: Constructor): ReturnType & { 6 | mixin: T; 7 | }; 8 | export default Mixin; 9 | export { WithDefault, Cached, HasInstance, ApplyDefault, Dedupe }; 10 | declare function WithDefault(classFactory: T, Default: Constructor): MixinFunction; 11 | declare function Cached(classFactory: T): (Base: Constructor) => any; 12 | declare function HasInstance(classFactory: T): MixinFunction; 13 | declare function ApplyDefault(classFactory: T): T; 14 | declare function Dedupe(classFactory: T): MixinFunction; 15 | //# sourceMappingURL=Mixin.d.ts.map -------------------------------------------------------------------------------- /dist/utils.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAIA,qBAAa,aAAa;IACzB,CAAC,wBAAgB;IACjB,GAAG,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM;IAIxB,GAAG,CAAC,IAAI,EAAE,MAAM;IAGhB,GAAG,CAAC,IAAI,EAAE,MAAM;CAGhB;AAGD,wBAAgB,eAAe,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,CAKpD;AAQD,wBAAgB,aAAa,CAAC,CAAC,SAAS,EAAE,EACzC,GAAG,EAAE,CAAC,EACN,GAAG,EAAE,MAAM,CAAC,EACZ,aAAa,EAAE,kBAAkB,EACjC,SAAS,UAAQ,GACf,IAAI,CAKN;AAED,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,GAAG,IAAI,CAYpG;AA2BD,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,MAAM,GAAG,kBAAkB,EACvE,GAAG,EAAE,CAAC,EACN,GAAG,CAAC,EAAE,MAAM,CAAC,EACb,SAAS,UAAO,GACd,OAAO,CAaT;AAED,+DAA+D;AAC/D,wBAAgB,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,WAShD;AAED,uEAAuE;AACvE,wBAAgB,eAAe,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,QAS7E;AAED,wBAAgB,8BAA8B,CAC7C,SAAS,EAAE,MAAM,EACjB,EAAC,sBAAsB,EAAE,EAAC,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAC,EAAC,EAAE,GAAG,QAmBnE;AAED,wBAAgB,2BAA2B,CAC1C,IAAI,EAAE,GAAG,EACT,EAAC,sBAAsB,EAAE,EAAC,QAAQ,EAAE,UAAU,EAAE,YAAY,EAAC,EAAC,EAAE,GAAG,EACnE,eAAe,CAAC,EAAE,CAAC,MAAM,GAAG,MAAM,CAAC,EAAE,QAwBrC"} -------------------------------------------------------------------------------- /dist/Constructor.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Constructor.d.ts","sourceRoot":"","sources":["../src/Constructor.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,MAAM,MAAM,WAAW,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE,MAAM,GAAG,EAAE,IAAI,CAAC,KAAK,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,MAAM,CAAA;AAEzG;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,GAAG,MAAM,EAAE,MAAM,GAAG,EAAE,EAAE,IAAI,EAAE,cAAc,CAAC,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC,CAAC,GAAG,MAAM,CAEvG;AAED;;;;;;;;GAQG;AACH,MAAM,MAAM,mBAAmB,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE,MAAM,GAAG,EAAE,IAAI,CAAC,QAAQ,MAAM,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,GAChH,MAAM,CAAA;AAEP;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,CAAC,GAAG,MAAM,EAAE,MAAM,GAAG,EAAE,EAC1D,IAAI,EAAE,cAAc,CAAC,GAAG,CAAC,GACvB,mBAAmB,CAAC,CAAC,CAAC,GAAG,MAAM,CAEjC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,MAAM,cAAc,CAAC,CAAC,GAAG,MAAM,EAAE,CAAC,SAAS,GAAG,EAAE,GAAG,GAAG,EAAE,EAAE,MAAM,GAAG,EAAE,IACxE,WAAW,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,GACzB,mBAAmB,CAAC,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC,CAAA;AAEpC;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,GAAG,MAAM,EAAE,MAAM,GAAG,EAAE,EAAE,IAAI,EAAE,cAAc,CAAC,GAAG,CAAC,GAAG,cAAc,CAAC,CAAC,CAAC,GAAG,MAAM,CAE7G"} -------------------------------------------------------------------------------- /dist/Constructor.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Cast any constructor type (abstract or not) into a specific Constructor type. 3 | * Useful for forcing type checks inside of mixins for example. This is unsafe: 4 | * you can incorrectly cast one constructor into an unrelated constructor type, 5 | * so use with care. 6 | */ 7 | export function Constructor(Ctor) { 8 | return Ctor; 9 | } 10 | /** 11 | * Cast any constructor type (abstract or not) into a specific 12 | * AbstractConstructor type. Useful for forcing type checks inside of mixins 13 | * for example. This is unsafe: you can incorrectly cast one constructor into an 14 | * unrelated constructor type, so use with care. 15 | */ 16 | export function AbstractConstructor(Ctor) { 17 | return Ctor; 18 | } 19 | /** 20 | * Cast any constructor type (abstract or not) into a specific 21 | * AnyConstructor type. Useful for forcing type checks inside of mixins 22 | * for example. This is unsafe: you can incorrectly cast one constructor into an 23 | * unrelated constructor type, so use with care. 24 | */ 25 | export function AnyConstructor(Ctor) { 26 | return Ctor; 27 | } 28 | //# sourceMappingURL=Constructor.js.map -------------------------------------------------------------------------------- /src/tests/Counter2.js: -------------------------------------------------------------------------------- 1 | // show how to do something similar to "friend" in C++ or "package protected" 2 | // in Java, using intentionally shared class brands 3 | 4 | import {Class} from '../Class.js' 5 | 6 | // an empty object used as a brand key by the Class() helper 7 | // 8 | // NOTE Too bad Symbols aren't supported by WeakMaps, otherwise we could use a 9 | // Symbol here, which would be cleaner. 10 | let FriendKey = {} 11 | 12 | const Counter2 = Class( 13 | 'Counter2', 14 | ({Private}) => ({ 15 | value() { 16 | return Private(this).count 17 | }, 18 | 19 | private: { 20 | count: 0, 21 | }, 22 | 23 | protected: { 24 | increment() { 25 | Private(this).count++ 26 | }, 27 | }, 28 | }), 29 | FriendKey, 30 | ) 31 | 32 | // note how Incrementor2 does not extend from Counter2 33 | const Incrementor2 = Class( 34 | 'Incrementor2', 35 | ({Private, Protected}) => ({ 36 | constructor(counter) { 37 | Private(this).counter = counter 38 | }, 39 | 40 | increment() { 41 | const counter = Private(this).counter 42 | Protected(counter).increment() 43 | }, 44 | }), 45 | FriendKey, 46 | ) 47 | 48 | export {Counter2, Incrementor2} 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Joseph Orbegoso Pea (joe@trusktr.io) 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /dist/tests/Counter2.js: -------------------------------------------------------------------------------- 1 | // show how to do something similar to "friend" in C++ or "package protected" 2 | // in Java, using intentionally shared class brands 3 | import { Class } from '../Class.js'; 4 | // an empty object used as a brand key by the Class() helper 5 | // 6 | // NOTE Too bad Symbols aren't supported by WeakMaps, otherwise we could use a 7 | // Symbol here, which would be cleaner. 8 | let FriendKey = {}; 9 | const Counter2 = Class('Counter2', ({ Private }) => ({ 10 | value() { 11 | return Private(this).count; 12 | }, 13 | private: { 14 | count: 0, 15 | }, 16 | protected: { 17 | increment() { 18 | Private(this).count++; 19 | }, 20 | }, 21 | }), FriendKey); 22 | // note how Incrementor2 does not extend from Counter2 23 | const Incrementor2 = Class('Incrementor2', ({ Private, Protected }) => ({ 24 | constructor(counter) { 25 | Private(this).counter = counter; 26 | }, 27 | increment() { 28 | const counter = Private(this).counter; 29 | Protected(counter).increment(); 30 | }, 31 | }), FriendKey); 32 | export { Counter2, Incrementor2 }; 33 | //# sourceMappingURL=Counter2.js.map -------------------------------------------------------------------------------- /src/native.test.ts: -------------------------------------------------------------------------------- 1 | // this shows the difference between our version of newless and the original 2 | // (see ./native.js) 3 | 4 | import {native} from './native.js' 5 | 6 | const test = it 7 | 8 | describe('native helper (newless)', () => { 9 | test('explain original behavior (conditions negated to pass)', () => { 10 | class Foo {} 11 | 12 | const _Foo = native(Foo) 13 | expect(!(_Foo.prototype === Foo.prototype)).toBeTruthy() 14 | expect(!(_Foo.prototype.constructor === Foo)).toBeTruthy() 15 | 16 | const f = new _Foo() 17 | expect(f instanceof Foo).toBeTruthy() 18 | expect(f instanceof _Foo).toBeTruthy() 19 | expect(!(f.constructor !== _Foo)).toBeTruthy() 20 | expect(!(f.constructor === Foo)).toBeTruthy() 21 | }) 22 | 23 | test('new behavior of our version of newless', () => { 24 | class Foo {} 25 | 26 | const _Foo = native(Foo) 27 | expect(_Foo.prototype === _Foo.prototype).toBeTruthy() 28 | expect(_Foo.prototype.constructor === _Foo).toBeTruthy() 29 | 30 | const f = new _Foo() 31 | expect(f instanceof Foo).toBeTruthy() 32 | expect(f instanceof _Foo).toBeTruthy() 33 | expect(f.constructor === _Foo).toBeTruthy() 34 | expect(f.constructor !== Foo).toBeTruthy() 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /src/tests/extending-builtins.test.js: -------------------------------------------------------------------------------- 1 | import {Class} from '../Class.js' 2 | import {native} from '../native.js' 3 | 4 | const test = it 5 | 6 | describe('extending builtins', () => { 7 | test('extending native Array', () => { 8 | const MyArray = Class().extends(native(Array), (Public, Protected, Private) => ({ 9 | constructor(...args) { 10 | const self = super.constructor(...args) 11 | self.__proto__ = MyArray.prototype 12 | 13 | Private(self).message = 'I am Array!' 14 | 15 | return self 16 | }, 17 | add(...args) { 18 | return Protected(this).add(...args) 19 | }, 20 | protected: { 21 | add(...args) { 22 | return Public(this).push(...args) 23 | }, 24 | }, 25 | 26 | showMessage() { 27 | return Private(this).message 28 | }, 29 | })) 30 | 31 | const a = new MyArray() 32 | expect(a instanceof Array).toBeTruthy() 33 | expect(a instanceof MyArray).toBeTruthy() 34 | 35 | expect(a.showMessage()).toBe('I am Array!') 36 | 37 | expect(a.add(1, 2, 3) === 3).toBeTruthy() 38 | expect(a.length === 3).toBeTruthy() 39 | expect(a.concat(4, 5, 6).length === 6).toBeTruthy() 40 | expect(a.concat(4, 5, 6) instanceof MyArray).toBeTruthy() 41 | expect(Array.isArray(a)).toBeTruthy() 42 | }) 43 | }) 44 | -------------------------------------------------------------------------------- /dist/utils.d.ts: -------------------------------------------------------------------------------- 1 | export declare class WeakTwoWayMap { 2 | m: WeakMap; 3 | set(a: Object, b: Object): void; 4 | get(item: Object): any; 5 | has(item: Object): boolean; 6 | } 7 | export declare function getFunctionBody(fn: Function): string; 8 | export declare function setDescriptor(obj: T, key: keyof T, newDescriptor: PropertyDescriptor, inherited?: boolean): void; 9 | export declare function setDescriptors(obj: Object, newDescriptors: Record): void; 10 | export declare function propertyIsAccessor(obj: T, key?: keyof T, inherited?: boolean): boolean; 11 | /** Check if an object has the given prototype in its chain. */ 12 | export declare function hasPrototype(obj: any, proto: any): boolean; 13 | /** Copy all properties (as descriptors) from source to destination. */ 14 | export declare function copyDescriptors(source: Object, destination: Object, mod?: any): void; 15 | export declare function setDefaultPrototypeDescriptors(prototype: Object, { defaultClassDescriptor: { writable, enumerable, configurable } }: any): void; 16 | export declare function setDefaultStaticDescriptors(Ctor: any, { defaultClassDescriptor: { writable, enumerable, configurable } }: any, staticBlacklist?: (string | symbol)[]): void; 17 | //# sourceMappingURL=utils.d.ts.map -------------------------------------------------------------------------------- /dist/multiple.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"multiple.d.ts","sourceRoot":"","sources":["../src/multiple.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,kBAAkB,CAAA;AAwBjD,aAAK,oBAAoB;IACxB,iCAAiC,sCAAsC;IACvE,oBAAoB,yBAAyB;IAQ7C,kCAAkC,uCAAuC;CACzE;AAED,KAAK,eAAe,GAAG;IACtB,MAAM,EAAE,oBAAoB,CAAA;CAC5B,CAAA;AAED,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,eAAe,IA4BlC,CAAC,SAAS,WAAW,EAAE,cAAc,CAAC,KAAG,eAAe,CAAC,CAAC,CAAC,CAepF;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,QAAQ,GA3BK,CAAC,SAAS,WAAW,EAAE,cAAc,CAAC,KAAG,eAAe,CAAC,CAAC,CA2BwB,CAAA;AA4U5G,KAAK,KAAK,CAAC,CAAC,SAAS,GAAG,EAAE,IAAI,CAAC,CAAC,GAAG,IAAI,EAAE,CAAC,KAAK,GAAG,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,GAAG,IAAI,EAAE,MAAM,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,KAAK,CAAA;AACzG,KAAK,UAAU,CAAC,CAAC,SAAS,WAAW,CAAC,GAAG,CAAC,EAAE,IAAI,WAAW,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AAClE,KAAK,WAAW,CAAC,CAAC,SAAS,WAAW,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,IAAI;IACnD,CAAC,EAAE,UAAU,CAAC,CAAA;IACd,CAAC,EAAE,WAAW,CACb,KAAK,CAAC,CAAC,CAAC,EACR;SACE,CAAC,IAAI,MAAM,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,GAAG,CAAC,SAAS,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KAC3F,CACD,CAAA;CACD,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAA;AAEhC,KAAK,eAAe,CAAC,CAAC,IAAI,CAAC,SAAS,EAAE,GAAG,CAAC,SAAS,CAAC,GACjD,OAAO,MAAM,GACb,CAAC,SAAS,WAAW,EAAE,GACvB,UAAU,CAAC,CAAC,CAAC,GACb,OAAO,MAAM,CAAA"} -------------------------------------------------------------------------------- /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: Build and Test 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | runs-on: ${{ matrix.operating-system }} 8 | 9 | strategy: 10 | matrix: 11 | # TODO get testing working in Windows. Help wanted! 12 | # windows-latest 13 | operating-system: [ubuntu-latest, macos-latest] 14 | 15 | steps: 16 | - uses: actions/checkout@v1 17 | - name: Use Node.js latest 18 | uses: actions/setup-node@v3 19 | with: 20 | node-version: latest 21 | - name: install 22 | run: | 23 | npm i 24 | - name: check formatting 25 | run: | 26 | npm run prettier:check 27 | - name: build 28 | run: | 29 | npm run clean 30 | npm run build 31 | - name: test 32 | run: | 33 | npm test 34 | - name: check repo is clean 35 | # skip this check in windows for now, as the build outputs may get slightly modified in Windows, which we want to fix. 36 | if: runner.os != 'Windows' 37 | run: | 38 | git add . && git diff --quiet && git diff --cached --quiet 39 | env: 40 | CI: true 41 | -------------------------------------------------------------------------------- /dist/multiple.d.ts: -------------------------------------------------------------------------------- 1 | import type { Constructor } from './Constructor.js'; 2 | declare enum ImplementationMethod { 3 | PROXIES_ON_INSTANCE_AND_PROTOTYPE = "PROXIES_ON_INSTANCE_AND_PROTOTYPE", 4 | PROXIES_ON_PROTOTYPE = "PROXIES_ON_PROTOTYPE", 5 | PROXY_AFTER_INSTANCE_AND_PROTOTYPE = "PROXY_AFTER_INSTANCE_AND_PROTOTYPE" 6 | } 7 | type MultipleOptions = { 8 | method: ImplementationMethod; 9 | }; 10 | export declare function makeMultipleHelper(options?: MultipleOptions): (...classes: T) => CombinedClasses; 11 | /** 12 | * Mixes the given classes into a single class. This is useful for multiple 13 | * inheritance. 14 | * 15 | * @example 16 | * class Foo {} 17 | * class Bar {} 18 | * class Baz {} 19 | * class MyClass extends multiple(Foo, Bar, Baz) {} 20 | */ 21 | export declare const multiple: (...classes: T) => CombinedClasses; 22 | type Shift = ((...args: T) => any) extends (_: any, ...args: infer R) => any ? R : never; 23 | type MixedArray[]> = _MixedArray; 24 | type _MixedArray[], U> = { 25 | 0: new () => U; 26 | 1: _MixedArray, { 27 | [K in keyof InstanceType | keyof U]: K extends keyof U ? U[K] : InstanceType[K]; 28 | }>; 29 | }[T['length'] extends 0 ? 0 : 1]; 30 | type CombinedClasses = T extends [] | [undefined] ? typeof Object : T extends Constructor[] ? MixedArray : typeof Object; 31 | export {}; 32 | //# sourceMappingURL=multiple.d.ts.map -------------------------------------------------------------------------------- /src/tests/extending-native-classes.test.js: -------------------------------------------------------------------------------- 1 | import {Class} from '../Class.js' 2 | import {native} from '../native.js' 3 | 4 | const test = it 5 | 6 | describe('extending native classes', () => { 7 | test('extend native class, and using Super helper', () => { 8 | class Foo { 9 | constructor(msg) { 10 | this.message = msg 11 | } 12 | 13 | method() { 14 | return this.message 15 | } 16 | } 17 | 18 | // TODO auto-detect `class`es 19 | const Bar = Class().extends(native(Foo), ({Super}) => ({ 20 | constructor(msg) { 21 | Super(this).constructor(msg) 22 | 23 | this.message += '!' 24 | }, 25 | 26 | method() { 27 | return Super(this).method() 28 | }, 29 | })) 30 | 31 | const b = new Bar('it works') 32 | 33 | expect(b instanceof Bar).toBeTruthy() 34 | expect(b instanceof Foo).toBeTruthy() 35 | expect(b.method() === 'it works!').toBeTruthy() 36 | }) 37 | 38 | test('extend native class, and using native `super`', () => { 39 | class Foo { 40 | constructor(msg) { 41 | this.message = msg 42 | } 43 | 44 | method() { 45 | return this.message 46 | } 47 | } 48 | 49 | const Bar = Class().extends(native(Foo), { 50 | constructor(msg) { 51 | super.constructor(msg) 52 | 53 | this.message += '!' 54 | }, 55 | 56 | method() { 57 | return super.method() 58 | }, 59 | }) 60 | 61 | const b = new Bar('it works') 62 | 63 | expect(b instanceof Bar).toBeTruthy() 64 | expect(b instanceof Foo).toBeTruthy() 65 | expect(b.method() === 'it works!').toBeTruthy() 66 | }) 67 | }) 68 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "lowclass", 3 | "version": "8.0.2", 4 | "description": "JavaScript/TypeScript class inheritance tools.", 5 | "author": "Joe Pea ", 6 | "license": "MIT", 7 | "homepage": "http://github.com/trusktr/lowclass#readme", 8 | "type": "module", 9 | "main": "dist/index.js", 10 | "types": "dist/index.d.ts", 11 | "scripts": { 12 | "LUME SCRIPTS XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX": "", 13 | "clean": "lume clean", 14 | "build": "lume build", 15 | "dev": "lume dev", 16 | "typecheck": "lume typecheck", 17 | "typecheck:watch": "lume typecheckWatch", 18 | "test": "lume test", 19 | "test:watch": "lume test --watch", 20 | "prettier": "lume prettier", 21 | "prettier:check": "lume prettierCheck", 22 | "release:patch": "lume releasePatch", 23 | "release:minor": "lume releaseMinor", 24 | "release:major": "lume releaseMajor", 25 | "version": "lume versionHook", 26 | "postversion": "lume postVersionHook", 27 | "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX OTHER SCRIPTS": "", 28 | "serve": "browser-sync start --server ./ --files dist --port 12345" 29 | }, 30 | "devDependencies": { 31 | "@lume/cli": "^0.14.0", 32 | "browser-sync": "^2.26.7", 33 | "prettier": "3.0.3", 34 | "typescript": "^5.0.0" 35 | }, 36 | "repository": { 37 | "type": "git", 38 | "url": "git+ssh://git@github.com/trusktr/lowclass.git" 39 | }, 40 | "bugs": { 41 | "url": "https://github.com/trusktr/lowclass/issues" 42 | }, 43 | "keywords": [ 44 | "classes", 45 | "es5 classes", 46 | "classical inheritance", 47 | "classical", 48 | "prototype", 49 | "prototypical inheritance", 50 | "prototypes", 51 | "prototype classes", 52 | "private", 53 | "protected", 54 | "access specifiers", 55 | "access modifiers" 56 | ] 57 | } 58 | -------------------------------------------------------------------------------- /src/tests/friends.test.js: -------------------------------------------------------------------------------- 1 | import {Counter, Incrementor} from './Counter.js' 2 | import {Counter2, Incrementor2} from './Counter2.js' 3 | 4 | const test = it 5 | 6 | describe('Friends', () => { 7 | test(` 8 | functionality similar to "friend" in C++ or "package protected" in Java, 9 | by means of intentionally leaked access helpers 10 | `, () => { 11 | // shows that functionality similar to "friend" in C++ or "package 12 | // protected" can be done with lowclass. See `./Counter.js` to learn how it 13 | // works. 14 | 15 | // in a real-world scenario, counter might be used here locally... 16 | const counter = new Counter() 17 | 18 | // ...while incrementor might be passed to third party code. 19 | const incrementor = new Incrementor(counter) 20 | 21 | // show that we can only access what is public 22 | expect(counter.count).toBe(undefined) 23 | expect(counter.increment).toBe(undefined) 24 | expect(typeof counter.value).toBe('function') 25 | 26 | expect(incrementor.counter).toBe(undefined) 27 | expect(typeof incrementor.increment).toBe('function') 28 | 29 | // show that it works: 30 | expect(counter.value()).toBe(0) 31 | incrementor.increment() 32 | expect(counter.value()).toBe(1) 33 | incrementor.increment() 34 | expect(counter.value()).toBe(2) 35 | }) 36 | 37 | test(` 38 | functionality similar to "friend" in C++ or "package protected" in Java, 39 | by means of intentionally shared class brands 40 | `, () => { 41 | // shows that functionality similar to "friend" in C++ or "package 42 | // protected" can be done with lowclass. See `./Counter2.js` to learn how it 43 | // works. 44 | 45 | // in a real-world scenario, counter might be used here locally... 46 | const counter = new Counter2() 47 | 48 | // ...while incrementor might be passed to third party code. 49 | const incrementor = new Incrementor2(counter) 50 | 51 | // show that we can only access what is public 52 | expect(counter.count).toBe(undefined) 53 | expect(counter.increment).toBe(undefined) 54 | expect(typeof counter.value).toBe('function') 55 | 56 | expect(incrementor.counter).toBe(undefined) 57 | expect(typeof incrementor.increment).toBe('function') 58 | 59 | // show that it works: 60 | expect(counter.value()).toBe(0) 61 | incrementor.increment() 62 | expect(counter.value()).toBe(1) 63 | incrementor.increment() 64 | expect(counter.value()).toBe(2) 65 | }) 66 | }) 67 | -------------------------------------------------------------------------------- /src/tests/wrap-custom-classes.test.js: -------------------------------------------------------------------------------- 1 | import {Class} from '../Class.js' 2 | 3 | const test = it 4 | 5 | describe('wrap existing classes', () => { 6 | test('protected and private members for custom-made ES5 classes', () => { 7 | const Foo = Class(({Protected, Private}) => { 8 | // make and return our own es5-style base class, with Protected and 9 | // Private helpers in scope. 10 | 11 | function Foo() { 12 | this.foo = 'foo' 13 | } 14 | 15 | Foo.prototype = { 16 | constructor: Foo, 17 | test() { 18 | expect(this.foo === 'foo').toBeTruthy() 19 | expect(Private(this).bar === 'bar').toBeTruthy() 20 | expect(Protected(this).baz === 'baz').toBeTruthy() 21 | }, 22 | 23 | // define access just like with regular class definitions 24 | private: { 25 | bar: 'bar', 26 | }, 27 | protected: { 28 | baz: 'baz', 29 | }, 30 | } 31 | 32 | return Foo 33 | }) 34 | 35 | const foo = new Foo() 36 | foo.test() 37 | 38 | const Bar = Class(({Super, Private}) => { 39 | // make and return our own es5-style subclass 40 | 41 | const prototype = { 42 | __proto__: Foo.prototype, 43 | 44 | constructor: function () { 45 | Super(this).constructor() 46 | }, 47 | 48 | test() { 49 | super.test() 50 | expect(Private(this).who === 'you').toBeTruthy() 51 | }, 52 | 53 | private: { 54 | who: 'you', 55 | }, 56 | } 57 | 58 | prototype.constructor.prototype = prototype 59 | 60 | return prototype.constructor 61 | }) 62 | 63 | const bar = new Bar() 64 | bar.test() 65 | }) 66 | 67 | test('protected and private members for custom-made native ES6+ classes', () => { 68 | // wrap our own es6 native-style base class with access helpers in scope. 69 | const Lorem = Class( 70 | ({Protected, Private}) => 71 | class { 72 | constructor() { 73 | this.foo = 'foo' 74 | } 75 | 76 | test() { 77 | expect(this.foo === 'foo').toBeTruthy() 78 | expect(Private(this).bar === 'bar').toBeTruthy() 79 | expect(Protected(this).baz === 'baz').toBeTruthy() 80 | } 81 | 82 | get private() { 83 | return { 84 | bar: 'bar', 85 | } 86 | } 87 | 88 | get protected() { 89 | return { 90 | baz: 'baz', 91 | } 92 | } 93 | }, 94 | ) 95 | 96 | const lorem = new Lorem() 97 | lorem.test() 98 | 99 | // wrap our own es6 native-style subclass with the access helpers in scope 100 | const Ipsum = Class(({Private}) => { 101 | return class extends Lorem { 102 | test() { 103 | super.test() 104 | expect(Private(this).secret === 'he did it').toBeTruthy() 105 | } 106 | 107 | get private() { 108 | return { 109 | secret: 'he did it', 110 | } 111 | } 112 | } 113 | }) 114 | 115 | const ip = new Ipsum() 116 | ip.test() 117 | }) 118 | }) 119 | -------------------------------------------------------------------------------- /dist/Constructor.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Without type args, this is an easy shortcut for "any non-abstract constructor 3 | * that has any args and returns any type of object". 4 | * 5 | * With type args, define a non-abstract constructor type that returns a certain 6 | * instance type (optional), accepts certain args (optional, defaults to any 7 | * args for simplicity in cases like class-factory mixins), and has certain 8 | * static members (optional). 9 | */ 10 | export type Constructor = (new (...a: A) => T) & Static; 11 | /** 12 | * Cast any constructor type (abstract or not) into a specific Constructor type. 13 | * Useful for forcing type checks inside of mixins for example. This is unsafe: 14 | * you can incorrectly cast one constructor into an unrelated constructor type, 15 | * so use with care. 16 | */ 17 | export declare function Constructor(Ctor: AnyConstructor): Constructor & Static; 18 | /** 19 | * Without type args, this is an easy shortcut for "any abstract constructor 20 | * that has any args and returns any type of object". 21 | * 22 | * With type args, define an abstract constructor type that returns a certain 23 | * instance type (optional), accepts certain args (optional, defaults to any 24 | * args for simplicity in cases like class-factory mixins), and has certain 25 | * static members (optional). 26 | */ 27 | export type AbstractConstructor = (abstract new (...a: A) => T) & Static; 28 | /** 29 | * Cast any constructor type (abstract or not) into a specific 30 | * AbstractConstructor type. Useful for forcing type checks inside of mixins 31 | * for example. This is unsafe: you can incorrectly cast one constructor into an 32 | * unrelated constructor type, so use with care. 33 | */ 34 | export declare function AbstractConstructor(Ctor: AnyConstructor): AbstractConstructor & Static; 35 | /** 36 | * Combines Constructor and AbstractConstructor to support assigning any type of 37 | * constructor whether abstract or not. 38 | * 39 | * Without type args, this is an easy shortcut for "any constructor, abstract or not, 40 | * that has any args and returns any type of object". 41 | * 42 | * With type args, define a constructor type (abstract or not) that returns a 43 | * certain instance type (optional), accepts certain args (optional, defaults to 44 | * any args for simplicity in cases like class-factory mixins), and has certain 45 | * static members (optional). 46 | */ 47 | export type AnyConstructor = Constructor | AbstractConstructor; 48 | /** 49 | * Cast any constructor type (abstract or not) into a specific 50 | * AnyConstructor type. Useful for forcing type checks inside of mixins 51 | * for example. This is unsafe: you can incorrectly cast one constructor into an 52 | * unrelated constructor type, so use with care. 53 | */ 54 | export declare function AnyConstructor(Ctor: AnyConstructor): AnyConstructor & Static; 55 | //# sourceMappingURL=Constructor.d.ts.map -------------------------------------------------------------------------------- /src/Constructor.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Without type args, this is an easy shortcut for "any non-abstract constructor 3 | * that has any args and returns any type of object". 4 | * 5 | * With type args, define a non-abstract constructor type that returns a certain 6 | * instance type (optional), accepts certain args (optional, defaults to any 7 | * args for simplicity in cases like class-factory mixins), and has certain 8 | * static members (optional). 9 | */ 10 | export type Constructor = (new (...a: A) => T) & Static 11 | 12 | /** 13 | * Cast any constructor type (abstract or not) into a specific Constructor type. 14 | * Useful for forcing type checks inside of mixins for example. This is unsafe: 15 | * you can incorrectly cast one constructor into an unrelated constructor type, 16 | * so use with care. 17 | */ 18 | export function Constructor(Ctor: AnyConstructor): Constructor & Static { 19 | return Ctor as unknown as Constructor & Static 20 | } 21 | 22 | /** 23 | * Without type args, this is an easy shortcut for "any abstract constructor 24 | * that has any args and returns any type of object". 25 | * 26 | * With type args, define an abstract constructor type that returns a certain 27 | * instance type (optional), accepts certain args (optional, defaults to any 28 | * args for simplicity in cases like class-factory mixins), and has certain 29 | * static members (optional). 30 | */ 31 | export type AbstractConstructor = (abstract new (...a: A) => T) & 32 | Static 33 | 34 | /** 35 | * Cast any constructor type (abstract or not) into a specific 36 | * AbstractConstructor type. Useful for forcing type checks inside of mixins 37 | * for example. This is unsafe: you can incorrectly cast one constructor into an 38 | * unrelated constructor type, so use with care. 39 | */ 40 | export function AbstractConstructor( 41 | Ctor: AnyConstructor, 42 | ): AbstractConstructor & Static { 43 | return Ctor as unknown as AbstractConstructor & Static 44 | } 45 | 46 | /** 47 | * Combines Constructor and AbstractConstructor to support assigning any type of 48 | * constructor whether abstract or not. 49 | * 50 | * Without type args, this is an easy shortcut for "any constructor, abstract or not, 51 | * that has any args and returns any type of object". 52 | * 53 | * With type args, define a constructor type (abstract or not) that returns a 54 | * certain instance type (optional), accepts certain args (optional, defaults to 55 | * any args for simplicity in cases like class-factory mixins), and has certain 56 | * static members (optional). 57 | */ 58 | export type AnyConstructor = 59 | | Constructor 60 | | AbstractConstructor 61 | 62 | /** 63 | * Cast any constructor type (abstract or not) into a specific 64 | * AnyConstructor type. Useful for forcing type checks inside of mixins 65 | * for example. This is unsafe: you can incorrectly cast one constructor into an 66 | * unrelated constructor type, so use with care. 67 | */ 68 | export function AnyConstructor(Ctor: AnyConstructor): AnyConstructor & Static { 69 | return Ctor as unknown as AnyConstructor & Static 70 | } 71 | -------------------------------------------------------------------------------- /dist/Mixin.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Mixin.js","sourceRoot":"","sources":["../src/Mixin.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /src/WIP-test-lowclass-types.ts.off: -------------------------------------------------------------------------------- 1 | import {Class} from 'lowclass/dist/Class.js' 2 | 3 | const Animal = Class('Animal', { 4 | sound: '', 5 | constructor(sound: string) { 6 | this.sound = sound 7 | }, 8 | makeSound() { 9 | console.log(this.sound) 10 | }, 11 | }) 12 | 13 | const a = new Animal('') 14 | 15 | a.makeSound() 16 | 17 | const Dog = Class('Dog').extends(Animal, ({ Super }) => ({ 18 | constructor(size: 'small' | 'big') { 19 | if (size === 'small') Super(this).constructor('woof') 20 | if (size === 'big') Super(this).constructor('WOOF') 21 | }, 22 | 23 | // makeSound(d: number) { 24 | // console.log(this.sound, d) 25 | // }, 26 | makeSound() { 27 | Super(this).makeSound() 28 | console.log(this.sound) 29 | }, 30 | 31 | bark() { 32 | this.makeSound() 33 | }, 34 | other() { 35 | this.bark() 36 | }, 37 | })) 38 | type Dog = InstanceType 39 | 40 | const smallDog: Dog = new Dog('small') 41 | smallDog.bark() // "woof" 42 | 43 | const bigDog = new Dog('big') 44 | bigDog.bark() // "WOOF" 45 | 46 | bigDog.bark() 47 | bigDog.makeSound() 48 | 49 | const Foo = Class('Foo', ({ Public, Protected, Private }) => ({ 50 | constructor(s: string) { 51 | console.log(s) 52 | }, 53 | 54 | static: { 55 | staticProp: 123, 56 | 57 | staticMethod() { 58 | console.log(Foo.staticProp) // 123 59 | }, 60 | }, 61 | 62 | publicProp: 'blah', 63 | 64 | publicMethod(a: string) { 65 | // TODO TS, this should work like Foo.staticMethod() 66 | // this.constructor.staticMethod() 67 | 68 | console.log(a) 69 | }, 70 | 71 | protected: { 72 | protectedProp: 456, 73 | 74 | protectedMethod(a: string) { 75 | console.log(a) 76 | console.log(Protected(this).protectedProp) 77 | }, 78 | }, 79 | 80 | private: { 81 | privateProp: 789, 82 | 83 | privateMethod() { 84 | Public(this).publicProp 85 | Public(this).publicMethod('') // Should this be allowed? Or do we just the below line to work ? 86 | Public(this).publicMethod('') 87 | }, 88 | }, 89 | 90 | test() { 91 | this.publicMethod('') 92 | 93 | console.log(this.publicProp) // 'blah' 94 | 95 | let p = Protected(this) 96 | console.log(p) 97 | p.protectedMethod('') 98 | console.log(p.protectedProp) // 456 99 | 100 | Private(this).privateMethod() 101 | console.log(Private(this).privateProp) // 789 102 | return Private(this).privateProp 103 | }, 104 | })) 105 | 106 | let foo = new Foo('') 107 | 108 | console.log(foo.publicProp) 109 | // console.log(foo.protectedProp) 110 | // console.log(foo.privateProp) 111 | 112 | Foo.staticMethod() 113 | Foo.staticProp 114 | 115 | const Bar = Class('Bar').extends(Foo, ({ Public, Protected, Private, Super }) => ({ 116 | constructor(sound: string) { 117 | sound 118 | }, 119 | static: { 120 | derivedStatic: 10, 121 | }, 122 | private: { 123 | derivedPrivate: 10, 124 | }, 125 | protected: { 126 | derivedProtected: 10, 127 | }, 128 | derivedPublicMethod() { 129 | Protected(this).protectedMethod('') 130 | Protected(this).protectedProp 131 | Protected(this).derivedProtected 132 | Private(this).derivedPrivate 133 | this.test() 134 | this.test() 135 | }, 136 | test() { 137 | const b = {} 138 | 139 | // TODO TS This call should only allow `Super(this)` or `Super(Protected(this))`. 140 | Super(b).test() 141 | 142 | return Super(this).test() 143 | }, 144 | })) 145 | 146 | var bar = new Bar('') 147 | bar.derivedPublicMethod() 148 | bar.test() 149 | // bar.derivedPrivate // should be error 150 | Bar.derivedStatic 151 | 152 | Bar.staticMethod() 153 | -------------------------------------------------------------------------------- /dist/WIP-test-lowclass-types.ts.off: -------------------------------------------------------------------------------- 1 | import {Class} from 'lowclass/dist/Class.js' 2 | 3 | const Animal = Class('Animal', { 4 | sound: '', 5 | constructor(sound: string) { 6 | this.sound = sound 7 | }, 8 | makeSound() { 9 | console.log(this.sound) 10 | }, 11 | }) 12 | 13 | const a = new Animal('') 14 | 15 | a.makeSound() 16 | 17 | const Dog = Class('Dog').extends(Animal, ({ Super }) => ({ 18 | constructor(size: 'small' | 'big') { 19 | if (size === 'small') Super(this).constructor('woof') 20 | if (size === 'big') Super(this).constructor('WOOF') 21 | }, 22 | 23 | // makeSound(d: number) { 24 | // console.log(this.sound, d) 25 | // }, 26 | makeSound() { 27 | Super(this).makeSound() 28 | console.log(this.sound) 29 | }, 30 | 31 | bark() { 32 | this.makeSound() 33 | }, 34 | other() { 35 | this.bark() 36 | }, 37 | })) 38 | type Dog = InstanceType 39 | 40 | const smallDog: Dog = new Dog('small') 41 | smallDog.bark() // "woof" 42 | 43 | const bigDog = new Dog('big') 44 | bigDog.bark() // "WOOF" 45 | 46 | bigDog.bark() 47 | bigDog.makeSound() 48 | 49 | const Foo = Class('Foo', ({ Public, Protected, Private }) => ({ 50 | constructor(s: string) { 51 | console.log(s) 52 | }, 53 | 54 | static: { 55 | staticProp: 123, 56 | 57 | staticMethod() { 58 | console.log(Foo.staticProp) // 123 59 | }, 60 | }, 61 | 62 | publicProp: 'blah', 63 | 64 | publicMethod(a: string) { 65 | // TODO TS, this should work like Foo.staticMethod() 66 | // this.constructor.staticMethod() 67 | 68 | console.log(a) 69 | }, 70 | 71 | protected: { 72 | protectedProp: 456, 73 | 74 | protectedMethod(a: string) { 75 | console.log(a) 76 | console.log(Protected(this).protectedProp) 77 | }, 78 | }, 79 | 80 | private: { 81 | privateProp: 789, 82 | 83 | privateMethod() { 84 | Public(this).publicProp 85 | Public(this).publicMethod('') // Should this be allowed? Or do we just the below line to work ? 86 | Public(this).publicMethod('') 87 | }, 88 | }, 89 | 90 | test() { 91 | this.publicMethod('') 92 | 93 | console.log(this.publicProp) // 'blah' 94 | 95 | let p = Protected(this) 96 | console.log(p) 97 | p.protectedMethod('') 98 | console.log(p.protectedProp) // 456 99 | 100 | Private(this).privateMethod() 101 | console.log(Private(this).privateProp) // 789 102 | return Private(this).privateProp 103 | }, 104 | })) 105 | 106 | let foo = new Foo('') 107 | 108 | console.log(foo.publicProp) 109 | // console.log(foo.protectedProp) 110 | // console.log(foo.privateProp) 111 | 112 | Foo.staticMethod() 113 | Foo.staticProp 114 | 115 | const Bar = Class('Bar').extends(Foo, ({ Public, Protected, Private, Super }) => ({ 116 | constructor(sound: string) { 117 | sound 118 | }, 119 | static: { 120 | derivedStatic: 10, 121 | }, 122 | private: { 123 | derivedPrivate: 10, 124 | }, 125 | protected: { 126 | derivedProtected: 10, 127 | }, 128 | derivedPublicMethod() { 129 | Protected(this).protectedMethod('') 130 | Protected(this).protectedProp 131 | Protected(this).derivedProtected 132 | Private(this).derivedPrivate 133 | this.test() 134 | this.test() 135 | }, 136 | test() { 137 | const b = {} 138 | 139 | // TODO TS This call should only allow `Super(this)` or `Super(Protected(this))`. 140 | Super(b).test() 141 | 142 | return Super(this).test() 143 | }, 144 | })) 145 | 146 | var bar = new Bar('') 147 | bar.derivedPublicMethod() 148 | bar.test() 149 | // bar.derivedPrivate // should be error 150 | Bar.derivedStatic 151 | 152 | Bar.staticMethod() 153 | -------------------------------------------------------------------------------- /dist/utils.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /src/tests/empty-classes.test.js: -------------------------------------------------------------------------------- 1 | import {Class} from '../Class.js' 2 | 3 | const test = it 4 | 5 | describe('empty classes', () => { 6 | test('anonymous empty base classes', () => { 7 | const Constructor = Class() 8 | const instance = new Constructor() 9 | expect(instance instanceof Constructor).toBeTruthy() 10 | expect(Constructor.name === '').toBeTruthy() 11 | expect(Constructor.prototype.__proto__ === Object.prototype).toBeTruthy() 12 | }) 13 | 14 | test('named empty base class', () => { 15 | const Foo = Class('Foo') 16 | const foo = new Foo() 17 | expect(foo instanceof Foo).toBeTruthy() 18 | expect(Foo.name === 'Foo').toBeTruthy() 19 | expect(Foo.prototype.__proto__ === Object.prototype).toBeTruthy() 20 | }) 21 | 22 | test('anonymous non-empty base class', () => { 23 | const Dog = Class(() => ({ 24 | method() {}, 25 | })) 26 | 27 | expect(Dog.name === '').toBeTruthy() 28 | expect(Dog.prototype.__proto__ === Object.prototype).toBeTruthy() 29 | 30 | const dog = new Dog() 31 | expect(dog instanceof Dog).toBeTruthy() 32 | expect(typeof dog.method === 'function').toBeTruthy() 33 | }) 34 | 35 | test('named non-empty base class', () => { 36 | const Dog = Class('Dog', () => ({ 37 | method() {}, 38 | })) 39 | 40 | expect(Dog.name === 'Dog').toBeTruthy() 41 | expect(Dog.prototype.__proto__ === Object.prototype).toBeTruthy() 42 | 43 | const dog = new Dog() 44 | expect(dog instanceof Dog).toBeTruthy() 45 | expect(typeof dog.method === 'function').toBeTruthy() 46 | }) 47 | 48 | test('anonymous empty subclass', () => { 49 | const LivingThing = Class() 50 | const Alien = Class().extends(LivingThing) 51 | expect(Alien.name === '').toBeTruthy() 52 | expect(Alien.prototype.__proto__ === LivingThing.prototype).toBeTruthy() 53 | 54 | const a = new Alien() 55 | expect(a instanceof Alien).toBeTruthy() 56 | }) 57 | 58 | test('named empty subclass', () => { 59 | const LivingThing = Class('LivingThing') 60 | const Alien = Class('Alien').extends(LivingThing) 61 | expect(Alien.name === 'Alien').toBeTruthy() 62 | expect(Alien.prototype.__proto__ === LivingThing.prototype).toBeTruthy() 63 | 64 | const a = new Alien() 65 | expect(a instanceof Alien).toBeTruthy() 66 | }) 67 | 68 | test('anonymous non-empty subclass', () => { 69 | const LivingThing = Class(() => ({ 70 | method1() {}, 71 | })) 72 | const Alien = Class().extends(LivingThing, () => ({ 73 | method2() {}, 74 | })) 75 | expect(Alien.name === '').toBeTruthy() 76 | expect(Alien.prototype.__proto__ === LivingThing.prototype).toBeTruthy() 77 | 78 | const a = new Alien() 79 | expect(a instanceof Alien).toBeTruthy() 80 | expect(a.method1).toBeTruthy() 81 | expect(a.method2).toBeTruthy() 82 | }) 83 | 84 | test('named non-empty subclass', () => { 85 | const LivingThing = Class('LivingThing', () => ({ 86 | method1() {}, 87 | })) 88 | const Alien = Class('Alien').extends(LivingThing, () => ({ 89 | method2() {}, 90 | })) 91 | expect(Alien.name === 'Alien').toBeTruthy() 92 | expect(Alien.prototype.__proto__ === LivingThing.prototype).toBeTruthy() 93 | 94 | const a = new Alien() 95 | expect(a instanceof Alien).toBeTruthy() 96 | expect(typeof a.method1 === 'function').toBeTruthy() 97 | expect(typeof a.method2 === 'function').toBeTruthy() 98 | }) 99 | 100 | test('anonymous subclass with extends at the end', () => { 101 | const SeaCreature = Class(() => ({ 102 | method1() {}, 103 | })) 104 | 105 | const Shark = Class(() => ({ 106 | method2() {}, 107 | })).extends(SeaCreature) 108 | 109 | expect(Shark.name === '').toBeTruthy() 110 | expect(Shark.prototype.__proto__ === SeaCreature.prototype).toBeTruthy() 111 | 112 | const shark = new Shark() 113 | expect(shark instanceof Shark).toBeTruthy() 114 | expect(typeof shark.method1 === 'function').toBeTruthy() 115 | expect(typeof shark.method2 === 'function').toBeTruthy() 116 | }) 117 | 118 | test('named subclass with extends at the end', () => { 119 | const SeaCreature = Class(() => ({ 120 | method1() {}, 121 | })) 122 | 123 | const Shark = Class('Shark', () => ({ 124 | method2() {}, 125 | })).extends(SeaCreature) 126 | 127 | expect(Shark.name === 'Shark').toBeTruthy() 128 | expect(Shark.prototype.__proto__ === SeaCreature.prototype).toBeTruthy() 129 | 130 | const shark = new Shark() 131 | expect(shark instanceof Shark).toBeTruthy() 132 | expect(typeof shark.method1 === 'function').toBeTruthy() 133 | expect(typeof shark.method2 === 'function').toBeTruthy() 134 | }) 135 | }) 136 | -------------------------------------------------------------------------------- /dist/Class.d.ts: -------------------------------------------------------------------------------- 1 | import { Constructor } from './Constructor.js'; 2 | import type { Id } from './types.js'; 3 | type ImplementationKeys = 'static' | 'private' | 'protected'; 4 | type FunctionToConstructor = T extends (...a: infer A) => void ? new (...a: A) => TReturn : never; 5 | type ReplaceCtorReturn = T extends new (...a: infer A) => unknown ? new (...a: A) => TReturn : never; 6 | type ConstructorOrDefault = T extends { 7 | constructor: infer TCtor; 8 | } ? TCtor : () => void; 9 | type SuperType<_T, TSuper extends Constructor> = TSuper extends Constructor ? { 10 | constructor: (...a: A) => I; 11 | } & InstanceType : never; 12 | type SuperHelper = (self: T) => SuperType; 13 | type PrivateHelper = (self: T) => T extends { 14 | __: { 15 | private: infer TPrivate; 16 | }; 17 | } ? TPrivate : never; 18 | type PublicHelper = (self: T) => Omit; 19 | type ProtectedHelper = (self: T) => T extends { 20 | __: { 21 | protected: infer TProtected; 22 | }; 23 | } ? TProtected : never; 24 | type Statics = T extends { 25 | static: infer TStatic; 26 | } ? TStatic : {}; 27 | type SaveInheritedProtected = T extends { 28 | protected: infer TProtected; 29 | } ? TProtected : {}; 30 | type StaticsAndProtected = Id & { 31 | __: { 32 | protected: SaveInheritedProtected; 33 | }; 34 | }>; 35 | type ExtractInheritedProtected = T extends { 36 | __: infer TProtected; 37 | } ? TProtected : {}; 38 | type PickImplementationKeys = Pick>; 39 | type LowClassThis = Id & { 40 | __: PickImplementationKeys; 41 | }>; 42 | type OmitImplementationKeys = Omit; 43 | export declare const staticBlacklist: string[]; 44 | export declare class InvalidSuperAccessError extends Error { 45 | } 46 | export declare class InvalidAccessError extends Error { 47 | } 48 | export declare const Class: { 49 | (): typeof Object; 50 | (name: string): { 51 | extends(base: TBase, members: (helpers: { 52 | Super: SuperHelper; 53 | Public: PublicHelper; 54 | Protected: ProtectedHelper; 55 | Private: PrivateHelper; 56 | }) => T & Partial> & ThisType & ExtractInheritedProtected>>, brand?: object): T extends { 57 | constructor: infer _TCtor; 58 | } ? FunctionToConstructor, Id & OmitImplementationKeys>> & Id & Pick> : ReplaceCtorReturn>> & Id & Pick>; 59 | }; 60 | (name: string, members: (helpers: { 61 | Public: PublicHelper; 62 | Protected: ProtectedHelper; 63 | Private: PrivateHelper; 64 | Super: never; 65 | }) => T & ThisType>, brand?: object): FunctionToConstructor, Id>> & Id>; 66 | (name: string, members: T & ThisType>, brand?: object): FunctionToConstructor, Id>> & Id>; 67 | }; 68 | export declare function createClassHelper(options?: any): { 69 | (): typeof Object; 70 | (name: string): { 71 | extends(base: TBase, members: (helpers: { 72 | Super: SuperHelper; 73 | Public: PublicHelper; 74 | Protected: ProtectedHelper; 75 | Private: PrivateHelper; 76 | }) => T & Partial> & ThisType & ExtractInheritedProtected>>, brand?: object): T extends { 77 | constructor: infer _TCtor; 78 | } ? FunctionToConstructor, Id & OmitImplementationKeys>> & Id & Pick> : ReplaceCtorReturn>> & Id & Pick>; 79 | }; 80 | (name: string, members: (helpers: { 81 | Public: PublicHelper; 82 | Protected: ProtectedHelper; 83 | Private: PrivateHelper; 84 | Super: never; 85 | }) => T & ThisType>, brand?: object): FunctionToConstructor, Id>> & Id>; 86 | (name: string, members: T & ThisType>, brand?: object): FunctionToConstructor, Id>> & Id>; 87 | }; 88 | export {}; 89 | //# sourceMappingURL=Class.d.ts.map -------------------------------------------------------------------------------- /dist/Mixin.js: -------------------------------------------------------------------------------- 1 | // TODO no any types 2 | // TODO no @ts-ignore 3 | import { Class } from './Class.js'; 4 | export function Mixin(mixinFn, DefaultBase) { 5 | // XXX Maybe Cached should go last. 6 | // @ts-ignore 7 | mixinFn = Cached(mixinFn); 8 | // @ts-ignore TS v4 introduced a type error 9 | mixinFn = HasInstance(mixinFn); 10 | // @ts-ignore TS v4 introduced a type error 11 | mixinFn = Dedupe(mixinFn); 12 | // @ts-ignore TS v4 introduced a type error 13 | mixinFn = WithDefault(mixinFn, DefaultBase || Class()); 14 | mixinFn = ApplyDefault(mixinFn); 15 | // @ts-ignore 16 | return mixinFn(); 17 | } 18 | export default Mixin; 19 | export { WithDefault, Cached, HasInstance, ApplyDefault, Dedupe }; 20 | // TODO remove WithDefault, we can use default argument syntax instead, which is more clear and conventional 21 | function WithDefault(classFactory, Default) { 22 | // @ts-ignore 23 | return named(classFactory.name, (Base) => { 24 | Base = Base || Default; 25 | return classFactory(Base); 26 | }); 27 | } 28 | function Cached(classFactory) { 29 | const classCache = new WeakMap(); 30 | // @ts-ignore 31 | return named(classFactory.name, (Base) => { 32 | let Class = classCache.get(Base); 33 | if (!Class) { 34 | classCache.set(Base, (Class = classFactory(Base))); 35 | } 36 | return Class; 37 | }); 38 | } 39 | function HasInstance(classFactory) { 40 | let instanceofSymbol; 41 | // @ts-ignore 42 | return named(classFactory.name, (Base) => { 43 | const Class = classFactory(Base); 44 | if (typeof Symbol === 'undefined' || !Symbol.hasInstance) 45 | return Class; 46 | if (Object.getOwnPropertySymbols(Class).includes(Symbol.hasInstance)) 47 | return Class; 48 | if (!instanceofSymbol) 49 | instanceofSymbol = Symbol('instanceofSymbol'); 50 | Class[instanceofSymbol] = true; 51 | Object.defineProperty(Class, Symbol.hasInstance, { 52 | value: function hasInstance(obj) { 53 | // we do this check because a subclass of `Class` may not have 54 | // it's own `[Symbol.hasInstance]()` method, therefore `this` 55 | // will be the subclass, not this `Class`, when the prototype 56 | // lookup on the subclass finds the `[Symbol.hasInstance]()` 57 | // method of this `Class`. In this case, we don't want to run 58 | // our logic here, so we delegate to the super class of this 59 | // `Class` to take over with the instanceof check. In many 60 | // cases, the super class `[Symbol.hasInstance]()` method will 61 | // be `Function.prototype[Symbol.hasInstance]` which will 62 | // perform the standard check. 63 | if (this !== Class) 64 | // This is effectively a `super` call. 65 | return Class.__proto__[Symbol.hasInstance].call(this, obj); 66 | let currentProto = obj; 67 | while (currentProto) { 68 | const descriptor = Object.getOwnPropertyDescriptor(currentProto, 'constructor'); 69 | if (descriptor && descriptor.value && descriptor.value.hasOwnProperty(instanceofSymbol)) 70 | return true; 71 | currentProto = currentProto.__proto__; 72 | } 73 | return false; 74 | }, 75 | }); 76 | return Class; 77 | }); 78 | } 79 | // requires WithDefault or a classFactory that can accept no args 80 | function ApplyDefault(classFactory) { 81 | const DefaultClass = classFactory(); 82 | DefaultClass.mixin = classFactory; 83 | return classFactory; 84 | } 85 | // requires Cached 86 | function Dedupe(classFactory) { 87 | const map = new WeakMap(); 88 | // @ts-ignore 89 | return named(classFactory.name, (Base) => { 90 | if (hasMixin(Base, classFactory, map)) 91 | return Base; 92 | const Class = classFactory(Base); 93 | map.set(Class, classFactory); 94 | return Class; 95 | }); 96 | } 97 | function hasMixin(Class, mixin, map) { 98 | while (Class) { 99 | if (map.get(Class) === mixin) 100 | return true; 101 | Class = Class.__proto__; 102 | } 103 | return false; 104 | } 105 | function named(name, func) { 106 | try { 107 | Object.defineProperty(func, 'name', { 108 | ...Object.getOwnPropertyDescriptor(func, 'name'), 109 | value: name, 110 | }); 111 | } 112 | catch (e) { 113 | // do nohing in case the property is non-configurable. 114 | } 115 | return func; 116 | } 117 | //# sourceMappingURL=Mixin.js.map -------------------------------------------------------------------------------- /dist/Class.d.ts.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"Class.d.ts","sourceRoot":"","sources":["../src/Class.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /src/Mixin.ts: -------------------------------------------------------------------------------- 1 | // TODO no any types 2 | // TODO no @ts-ignore 3 | 4 | import {Class} from './Class.js' 5 | import type {Constructor} from './Constructor.js' 6 | 7 | // export type MixinFunction = (BaseClass: T) => T 8 | export type MixinFunction = >(BaseClass: T) => T 9 | export type MixinFunctionWithDefault = >(BaseClass?: T) => T 10 | 11 | // prettier-ignore 12 | export type MixinResult = 13 | Constructor & InstanceType> & TClass & TBase 14 | 15 | export function Mixin(mixinFn: T, DefaultBase?: Constructor): ReturnType & {mixin: T} { 16 | // XXX Maybe Cached should go last. 17 | // @ts-ignore 18 | mixinFn = Cached(mixinFn) 19 | // @ts-ignore TS v4 introduced a type error 20 | mixinFn = HasInstance(mixinFn) 21 | // @ts-ignore TS v4 introduced a type error 22 | mixinFn = Dedupe(mixinFn) 23 | // @ts-ignore TS v4 introduced a type error 24 | mixinFn = WithDefault(mixinFn, DefaultBase || Class()) 25 | mixinFn = ApplyDefault(mixinFn) 26 | 27 | // @ts-ignore 28 | return mixinFn() 29 | } 30 | 31 | export default Mixin 32 | export {WithDefault, Cached, HasInstance, ApplyDefault, Dedupe} 33 | 34 | // TODO remove WithDefault, we can use default argument syntax instead, which is more clear and conventional 35 | function WithDefault(classFactory: T, Default: Constructor) { 36 | // @ts-ignore 37 | return named(classFactory.name, (Base: Constructor) => { 38 | Base = Base || Default 39 | return classFactory(Base) 40 | }) 41 | } 42 | 43 | function Cached(classFactory: T) { 44 | const classCache = new WeakMap() 45 | 46 | // @ts-ignore 47 | return named(classFactory.name, (Base: Constructor) => { 48 | let Class = classCache.get(Base) 49 | 50 | if (!Class) { 51 | classCache.set(Base, (Class = classFactory(Base))) 52 | } 53 | 54 | return Class 55 | }) 56 | } 57 | 58 | function HasInstance(classFactory: T) { 59 | let instanceofSymbol: symbol 60 | 61 | // @ts-ignore 62 | return named(classFactory.name, (Base: Constructor) => { 63 | const Class = classFactory(Base) 64 | 65 | if (typeof Symbol === 'undefined' || !Symbol.hasInstance) return Class 66 | 67 | if (Object.getOwnPropertySymbols(Class).includes(Symbol.hasInstance)) return Class 68 | 69 | if (!instanceofSymbol) 70 | instanceofSymbol = Symbol('instanceofSymbol') 71 | 72 | // NOTE we could also use a WeakMap instead of placing a flag on the 73 | // Class directly. 74 | ;(Class as any)[instanceofSymbol] = true 75 | 76 | Object.defineProperty(Class, Symbol.hasInstance, { 77 | value: function hasInstance(obj: Object) { 78 | // we do this check because a subclass of `Class` may not have 79 | // it's own `[Symbol.hasInstance]()` method, therefore `this` 80 | // will be the subclass, not this `Class`, when the prototype 81 | // lookup on the subclass finds the `[Symbol.hasInstance]()` 82 | // method of this `Class`. In this case, we don't want to run 83 | // our logic here, so we delegate to the super class of this 84 | // `Class` to take over with the instanceof check. In many 85 | // cases, the super class `[Symbol.hasInstance]()` method will 86 | // be `Function.prototype[Symbol.hasInstance]` which will 87 | // perform the standard check. 88 | if (this !== Class) 89 | // This is effectively a `super` call. 90 | return (Class as any).__proto__[Symbol.hasInstance].call(this, obj) 91 | 92 | let currentProto = obj 93 | 94 | while (currentProto) { 95 | const descriptor = Object.getOwnPropertyDescriptor(currentProto, 'constructor') 96 | 97 | if (descriptor && descriptor.value && descriptor.value.hasOwnProperty(instanceofSymbol)) return true 98 | 99 | currentProto = (currentProto as any).__proto__ 100 | } 101 | 102 | return false 103 | }, 104 | }) 105 | 106 | return Class 107 | }) 108 | } 109 | 110 | // requires WithDefault or a classFactory that can accept no args 111 | function ApplyDefault(classFactory: T) { 112 | const DefaultClass = (classFactory as any)() 113 | ;(DefaultClass as any).mixin = classFactory 114 | return classFactory 115 | } 116 | 117 | // requires Cached 118 | function Dedupe(classFactory: T) { 119 | const map = new WeakMap() 120 | 121 | // @ts-ignore 122 | return named(classFactory.name, (Base: Constructor) => { 123 | if (hasMixin(Base, classFactory, map)) return Base 124 | 125 | const Class = classFactory(Base) 126 | map.set(Class, classFactory) 127 | return Class 128 | }) 129 | } 130 | 131 | function hasMixin(Class: Constructor, mixin: MixinFunction, map: WeakMap) { 132 | while (Class) { 133 | if (map.get(Class) === mixin) return true 134 | Class = (Class as any).__proto__ 135 | } 136 | 137 | return false 138 | } 139 | 140 | function named(name: string, func: T) { 141 | try { 142 | Object.defineProperty(func, 'name', { 143 | ...Object.getOwnPropertyDescriptor(func, 'name'), 144 | value: name, 145 | }) 146 | } catch (e) { 147 | // do nohing in case the property is non-configurable. 148 | } 149 | 150 | return func 151 | } 152 | -------------------------------------------------------------------------------- /dist/utils.js: -------------------------------------------------------------------------------- 1 | // TODO no any 2 | import { getInheritedDescriptor } from './getInheritedDescriptor.js'; 3 | export class WeakTwoWayMap { 4 | m = new WeakMap(); 5 | set(a, b) { 6 | this.m.set(a, b); 7 | this.m.set(b, a); 8 | } 9 | get(item) { 10 | return this.m.get(item); 11 | } 12 | has(item) { 13 | return this.m.has(item); 14 | } 15 | } 16 | // assumes the function opening, body, and closing are on separate lines 17 | export function getFunctionBody(fn) { 18 | const code = fn.toString().split('\n'); 19 | code.shift(); // remove opening line (function() {) 20 | code.pop(); // remove closing line (}) 21 | return code.join('\n'); 22 | } 23 | const descriptorDefaults = { 24 | enumerable: true, 25 | configurable: true, 26 | }; 27 | // makes it easier and less verbose to work with descriptors 28 | export function setDescriptor(obj, key, newDescriptor, inherited = false) { 29 | let currentDescriptor = inherited ? getInheritedDescriptor(obj, key) : Object.getOwnPropertyDescriptor(obj, key); 30 | newDescriptor = overrideDescriptor(currentDescriptor, newDescriptor); 31 | Object.defineProperty(obj, key, newDescriptor); 32 | } 33 | export function setDescriptors(obj, newDescriptors) { 34 | let newDescriptor; 35 | let currentDescriptor; 36 | const currentDescriptors = Object.getOwnPropertyDescriptors(obj); 37 | for (const key in newDescriptors) { 38 | newDescriptor = newDescriptors[key]; 39 | currentDescriptor = currentDescriptors[key]; 40 | newDescriptors[key] = overrideDescriptor(currentDescriptor, newDescriptor); 41 | } 42 | Object.defineProperties(obj, newDescriptors); 43 | } 44 | function overrideDescriptor(oldDescriptor, newDescriptor) { 45 | if (('get' in newDescriptor || 'set' in newDescriptor) && 46 | ('value' in newDescriptor || 'writable' in newDescriptor)) { 47 | throw new TypeError('cannot specify both accessors and a value or writable attribute'); 48 | } 49 | if (oldDescriptor) { 50 | if ('get' in newDescriptor || 'set' in newDescriptor) { 51 | delete oldDescriptor.value; 52 | delete oldDescriptor.writable; 53 | } 54 | else if ('value' in newDescriptor || 'writable' in newDescriptor) { 55 | delete oldDescriptor.get; 56 | delete oldDescriptor.set; 57 | } 58 | } 59 | return { ...descriptorDefaults, ...oldDescriptor, ...newDescriptor }; 60 | } 61 | // TODO use signature override 62 | export function propertyIsAccessor(obj, key, inherited = true) { 63 | let result = false; 64 | let descriptor; 65 | if (arguments.length === 1) { 66 | descriptor = obj; 67 | } 68 | else { 69 | descriptor = inherited ? getInheritedDescriptor(obj, key) : Object.getOwnPropertyDescriptor(obj, key); 70 | } 71 | if (descriptor && (descriptor.get || descriptor.set)) 72 | result = true; 73 | return result; 74 | } 75 | /** Check if an object has the given prototype in its chain. */ 76 | export function hasPrototype(obj, proto) { 77 | let currentProto = obj.__proto__; 78 | do { 79 | if (proto === currentProto) 80 | return true; 81 | currentProto = currentProto.__proto__; 82 | } while (currentProto); 83 | return false; 84 | } 85 | /** Copy all properties (as descriptors) from source to destination. */ 86 | export function copyDescriptors(source, destination, mod) { 87 | const props = Object.getOwnPropertyNames(source); 88 | let i = props.length; 89 | while (i--) { 90 | const prop = props[i]; 91 | const descriptor = Object.getOwnPropertyDescriptor(source, prop); 92 | if (mod) 93 | mod(descriptor); 94 | Object.defineProperty(destination, prop, descriptor); 95 | } 96 | } 97 | export function setDefaultPrototypeDescriptors(prototype, { defaultClassDescriptor: { writable, enumerable, configurable } }) { 98 | const descriptors = Object.getOwnPropertyDescriptors(prototype); 99 | let descriptor; 100 | for (const key in descriptors) { 101 | descriptor = descriptors[key]; 102 | // regular value 103 | if ('value' in descriptor || 'writable' in descriptor) { 104 | descriptor.writable = writable; 105 | } 106 | // accessor or regular value 107 | descriptor.enumerable = enumerable; 108 | descriptor.configurable = configurable; 109 | } 110 | setDescriptors(prototype, descriptors); 111 | } 112 | export function setDefaultStaticDescriptors(Ctor, { defaultClassDescriptor: { writable, enumerable, configurable } }, staticBlacklist) { 113 | const descriptors = Object.getOwnPropertyDescriptors(Ctor); 114 | let descriptor; 115 | for (const key in descriptors) { 116 | if (staticBlacklist && staticBlacklist.includes(key)) { 117 | delete descriptors[key]; 118 | continue; 119 | } 120 | descriptor = descriptors[key]; 121 | // regular value 122 | if ('value' in descriptor || 'writable' in descriptor) { 123 | descriptor.writable = writable; 124 | } 125 | // accessor or regular value 126 | descriptor.enumerable = enumerable; 127 | descriptor.configurable = configurable; 128 | } 129 | setDescriptors(Ctor, descriptors); 130 | } 131 | //# sourceMappingURL=utils.js.map -------------------------------------------------------------------------------- /dist/native.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"native.js","sourceRoot":"","sources":["../src/native.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | // TODO no any 2 | 3 | import {getInheritedDescriptor} from './getInheritedDescriptor.js' 4 | 5 | export class WeakTwoWayMap { 6 | m = new WeakMap() 7 | set(a: Object, b: Object) { 8 | this.m.set(a, b) 9 | this.m.set(b, a) 10 | } 11 | get(item: Object) { 12 | return this.m.get(item) 13 | } 14 | has(item: Object) { 15 | return this.m.has(item) 16 | } 17 | } 18 | 19 | // assumes the function opening, body, and closing are on separate lines 20 | export function getFunctionBody(fn: Function): string { 21 | const code = fn.toString().split('\n') 22 | code.shift() // remove opening line (function() {) 23 | code.pop() // remove closing line (}) 24 | return code.join('\n') 25 | } 26 | 27 | const descriptorDefaults = { 28 | enumerable: true, 29 | configurable: true, 30 | } 31 | 32 | // makes it easier and less verbose to work with descriptors 33 | export function setDescriptor( 34 | obj: T, 35 | key: keyof T, 36 | newDescriptor: PropertyDescriptor, 37 | inherited = false, 38 | ): void { 39 | let currentDescriptor = inherited ? getInheritedDescriptor(obj, key) : Object.getOwnPropertyDescriptor(obj, key) 40 | 41 | newDescriptor = overrideDescriptor(currentDescriptor, newDescriptor) 42 | Object.defineProperty(obj, key, newDescriptor) 43 | } 44 | 45 | export function setDescriptors(obj: Object, newDescriptors: Record): void { 46 | let newDescriptor: PropertyDescriptor 47 | let currentDescriptor: PropertyDescriptor 48 | const currentDescriptors = Object.getOwnPropertyDescriptors(obj) 49 | 50 | for (const key in newDescriptors) { 51 | newDescriptor = newDescriptors[key]! 52 | currentDescriptor = currentDescriptors[key]! 53 | newDescriptors[key] = overrideDescriptor(currentDescriptor, newDescriptor) 54 | } 55 | 56 | Object.defineProperties(obj, newDescriptors) 57 | } 58 | 59 | function overrideDescriptor( 60 | oldDescriptor: PropertyDescriptor | undefined, 61 | newDescriptor: PropertyDescriptor, 62 | ): PropertyDescriptor { 63 | if ( 64 | ('get' in newDescriptor || 'set' in newDescriptor) && 65 | ('value' in newDescriptor || 'writable' in newDescriptor) 66 | ) { 67 | throw new TypeError('cannot specify both accessors and a value or writable attribute') 68 | } 69 | 70 | if (oldDescriptor) { 71 | if ('get' in newDescriptor || 'set' in newDescriptor) { 72 | delete oldDescriptor.value 73 | delete oldDescriptor.writable 74 | } else if ('value' in newDescriptor || 'writable' in newDescriptor) { 75 | delete oldDescriptor.get 76 | delete oldDescriptor.set 77 | } 78 | } 79 | 80 | return {...descriptorDefaults, ...oldDescriptor, ...newDescriptor} 81 | } 82 | 83 | // TODO use signature override 84 | export function propertyIsAccessor( 85 | obj: T, 86 | key?: keyof T, 87 | inherited = true, 88 | ): boolean { 89 | let result = false 90 | let descriptor: PropertyDescriptor | undefined 91 | 92 | if (arguments.length === 1) { 93 | descriptor = obj 94 | } else { 95 | descriptor = inherited ? getInheritedDescriptor(obj, key!) : Object.getOwnPropertyDescriptor(obj, key!) 96 | } 97 | 98 | if (descriptor && (descriptor.get || descriptor.set)) result = true 99 | 100 | return result 101 | } 102 | 103 | /** Check if an object has the given prototype in its chain. */ 104 | export function hasPrototype(obj: any, proto: any) { 105 | let currentProto = obj.__proto__ 106 | 107 | do { 108 | if (proto === currentProto) return true 109 | currentProto = currentProto.__proto__ 110 | } while (currentProto) 111 | 112 | return false 113 | } 114 | 115 | /** Copy all properties (as descriptors) from source to destination. */ 116 | export function copyDescriptors(source: Object, destination: Object, mod?: any) { 117 | const props = Object.getOwnPropertyNames(source) 118 | let i = props.length 119 | while (i--) { 120 | const prop = props[i]! 121 | const descriptor = Object.getOwnPropertyDescriptor(source, prop) 122 | if (mod) mod(descriptor) 123 | Object.defineProperty(destination, prop, descriptor!) 124 | } 125 | } 126 | 127 | export function setDefaultPrototypeDescriptors( 128 | prototype: Object, 129 | {defaultClassDescriptor: {writable, enumerable, configurable}}: any, 130 | ) { 131 | const descriptors = Object.getOwnPropertyDescriptors(prototype) 132 | let descriptor 133 | 134 | for (const key in descriptors) { 135 | descriptor = descriptors[key]! 136 | 137 | // regular value 138 | if ('value' in descriptor || 'writable' in descriptor) { 139 | descriptor.writable = writable 140 | } 141 | 142 | // accessor or regular value 143 | descriptor.enumerable = enumerable 144 | descriptor.configurable = configurable 145 | } 146 | 147 | setDescriptors(prototype, descriptors) 148 | } 149 | 150 | export function setDefaultStaticDescriptors( 151 | Ctor: any, 152 | {defaultClassDescriptor: {writable, enumerable, configurable}}: any, 153 | staticBlacklist?: (string | symbol)[], 154 | ) { 155 | const descriptors = Object.getOwnPropertyDescriptors(Ctor) 156 | let descriptor 157 | 158 | for (const key in descriptors) { 159 | if (staticBlacklist && staticBlacklist.includes(key)) { 160 | delete descriptors[key] 161 | continue 162 | } 163 | 164 | descriptor = descriptors[key]! 165 | 166 | // regular value 167 | if ('value' in descriptor || 'writable' in descriptor) { 168 | descriptor.writable = writable 169 | } 170 | 171 | // accessor or regular value 172 | descriptor.enumerable = enumerable 173 | descriptor.configurable = configurable 174 | } 175 | 176 | setDescriptors(Ctor, descriptors) 177 | } 178 | -------------------------------------------------------------------------------- /src/tests/readme-examples.test.js: -------------------------------------------------------------------------------- 1 | import {Class} from '../Class.js' 2 | 3 | const test = it 4 | 5 | describe('README examples', () => { 6 | test('use a real protected member instead of the underscore convention, ES2015 classes', () => { 7 | // an alias, which can be semantically more meaningful when wrapping a native 8 | // `class` that already contains the "class" keyword. 9 | const protect = Class 10 | 11 | const Thing = protect( 12 | ({Protected}) => 13 | class { 14 | constructor() { 15 | // stop using underscore and make it truly protected: 16 | Protected(this).protectedProperty = 'yoohoo' 17 | } 18 | 19 | someMethod() { 20 | return Protected(this).protectedProperty 21 | } 22 | }, 23 | ) 24 | 25 | const t = new Thing() 26 | 27 | expect(t.someMethod()).toBe('yoohoo') 28 | 29 | // the value is not publicly accessible! 30 | expect(t.protectedProperty).toBe(undefined) 31 | 32 | const Something = protect( 33 | ({Protected}) => 34 | class extends Thing { 35 | otherMethod() { 36 | // access the inherited actually-protected member 37 | return Protected(this).protectedProperty 38 | } 39 | }, 40 | ) 41 | 42 | const s = new Something() 43 | expect(s.protectedProperty).toBe(undefined) 44 | expect(s.otherMethod()).toBe('yoohoo') 45 | }) 46 | 47 | test('use a real protected member instead of the underscore convention, ES5 classes', () => { 48 | // an alias, which semantically more meaningful when wrapping a native 49 | // `class` that already contains the "class" keyword. 50 | const protect = Class 51 | 52 | const Thing = protect(({Protected}) => { 53 | function Thing() { 54 | Protected(this).protectedProperty = 'yoohoo' 55 | } 56 | 57 | Thing.prototype = { 58 | constructor: Thing, 59 | 60 | someMethod() { 61 | return Protected(this).protectedProperty 62 | }, 63 | } 64 | 65 | return Thing 66 | }) 67 | 68 | const t = new Thing() 69 | 70 | expect(t.someMethod()).toBe('yoohoo') 71 | 72 | // the value is not publicly accessible! 73 | expect(t.protectedProperty).toBe(undefined) 74 | 75 | const Something = protect(({Protected}) => { 76 | function Something() { 77 | Thing.call(this) 78 | } 79 | 80 | Something.prototype = { 81 | __proto__: Thing.prototype, 82 | constructor: Something, 83 | 84 | otherMethod() { 85 | // access the inherited actually-protected member 86 | return Protected(this).protectedProperty 87 | }, 88 | } 89 | 90 | return Something 91 | }) 92 | 93 | const s = new Something() 94 | expect(s.protectedProperty).toBe(undefined) 95 | expect(s.otherMethod()).toBe('yoohoo') 96 | }) 97 | 98 | test('no access of parent private data in subclass', () => { 99 | const Thing = Class(({Private}) => ({ 100 | constructor() { 101 | Private(this).privateProperty = 'yoohoo' 102 | }, 103 | 104 | someMethod() { 105 | return Private(this).privateProperty 106 | }, 107 | 108 | changeIt() { 109 | Private(this).privateProperty = 'oh yeah' 110 | }, 111 | })) 112 | 113 | const Something = Class().extends(Thing, ({Private}) => ({ 114 | otherMethod() { 115 | return Private(this).privateProperty 116 | }, 117 | 118 | makeItSo() { 119 | Private(this).privateProperty = 'it is so' 120 | }, 121 | })) 122 | 123 | const instance = new Something() 124 | 125 | expect(instance.someMethod()).toBe('yoohoo') 126 | expect(instance.otherMethod()).toBe(undefined) 127 | 128 | instance.changeIt() 129 | expect(instance.someMethod()).toBe('oh yeah') 130 | expect(instance.otherMethod()).toBe(undefined) 131 | 132 | instance.makeItSo() 133 | expect(instance.someMethod()).toBe('oh yeah') 134 | expect(instance.otherMethod()).toBe('it is so') 135 | }) 136 | 137 | test('no access of parent private data in subclass', () => { 138 | const Thing = Class(({Private}) => ({ 139 | constructor() { 140 | Private(this).privateProperty = 'yoohoo' 141 | }, 142 | })) 143 | 144 | const Something = Thing.subclass(({Private}) => ({ 145 | otherMethod() { 146 | return Private(this).privateProperty 147 | }, 148 | })) 149 | 150 | const something = new Something() 151 | 152 | expect(something.otherMethod()).toBe(undefined) 153 | }) 154 | 155 | test('private inheritance', () => { 156 | const Counter = Class(({Private}) => ({ 157 | private: { 158 | // this is a prototype prop, the initial value will be inherited by subclasses 159 | count: 0, 160 | 161 | increment() { 162 | this.count++ 163 | }, 164 | }, 165 | 166 | tick() { 167 | Private(this).increment() 168 | 169 | return Private(this).count 170 | }, 171 | 172 | getCountValue() { 173 | return Private(this).count 174 | }, 175 | })) 176 | 177 | const DoubleCounter = Counter.subclass(({Private}) => ({ 178 | doubleTick() { 179 | Private(this).increment() 180 | Private(this).increment() 181 | 182 | return Private(this).count 183 | }, 184 | 185 | getDoubleCountValue() { 186 | return Private(this).count 187 | }, 188 | })) 189 | 190 | const counter = new Counter() 191 | 192 | expect(counter.tick()).toBe(1) 193 | 194 | const doubleCounter = new DoubleCounter() 195 | 196 | expect(doubleCounter.doubleTick()).toBe(2) 197 | expect(doubleCounter.tick()).toBe(1) 198 | 199 | expect(doubleCounter.doubleTick()).toBe(4) 200 | expect(doubleCounter.tick()).toBe(2) 201 | 202 | // There's a private `counter` member for the Counter class, and there's a 203 | // separate private `counter` member for the `DoubleCounter` class (the 204 | // initial value inherited from `Counter`): 205 | expect(doubleCounter.getDoubleCountValue()).not.toBe(counter.getCountValue()) 206 | expect(doubleCounter.getCountValue()).toBe(2) 207 | expect(doubleCounter.getDoubleCountValue()).toBe(4) 208 | }) 209 | }) 210 | -------------------------------------------------------------------------------- /src/tests/custom-elements.test.js: -------------------------------------------------------------------------------- 1 | import {Class} from '../Class.js' 2 | import {native} from '../native.js' 3 | 4 | describe('Custom Elements', () => { 5 | // example of extending HTMLElement for use with customElements.define 6 | // (Custom Elements) 7 | it('works with custom elements', () => { 8 | // full example, wraps builtin HTMLElement class with the native helper 9 | { 10 | const MyEL = Class().extends(native(HTMLElement), ({Super}) => ({ 11 | static: { 12 | observedAttributes: ['foo'], 13 | }, 14 | 15 | connected: false, 16 | disconnected: true, 17 | 18 | constructor() { 19 | // return is needed for it to work 20 | return Super(this).constructor() 21 | 22 | // native super works too 23 | //return super.constructor() 24 | }, 25 | 26 | connectedCallback() { 27 | this.connected = true 28 | this.disconnected = false 29 | }, 30 | 31 | disconnectedCallback() { 32 | this.connected = false 33 | this.disconnected = true 34 | }, 35 | 36 | attributeChangedCallback(attr, oldVal, newVal) { 37 | this[attr] = newVal 38 | }, 39 | })) 40 | 41 | customElements.define('my-el', MyEL) 42 | 43 | const el = document.createElement('my-el') 44 | 45 | document.body.appendChild(el) 46 | expect(el.connected).toBe(true) 47 | expect(el.disconnected).toBe(false) 48 | 49 | el.setAttribute('foo', 'bar') 50 | expect(el.foo).toBe('bar') 51 | 52 | document.body.removeChild(el) 53 | expect(el.connected).toBe(false) 54 | expect(el.disconnected).toBe(true) 55 | } 56 | 57 | // other ways to do it too: 58 | 59 | // with Reflect.construct and builtin HTMLElement, no native helper. 60 | // The native helper uses Reflect.construct internally to achieve a 61 | // similar effect. 62 | { 63 | const MyEl = Class().extends(window.HTMLElement, ({Super}) => ({ 64 | constructor() { 65 | // Reflect.construct is needed to be used manually if we 66 | // don't use the native helper 67 | return Reflect.construct(Super(this).constructor, [], this.constructor) 68 | 69 | // using native super would work here too 70 | //return Reflect.construct(super.constructor, [], this.constructor) 71 | 72 | // we could also construct HTMLElement directly 73 | //return Reflect.construct(HTMLElement, [], this.constructor) 74 | 75 | // don't use new.target, it doesn't work (for now at least) 76 | //return Reflect.construct(super.constructor, [], new.target) 77 | }, 78 | connectedCallback() { 79 | this.connected = true 80 | }, 81 | })) 82 | 83 | customElements.define('my-el1', MyEl) 84 | const el = new MyEl() 85 | document.body.appendChild(el) 86 | 87 | expect(el.connected).toBe(true) 88 | 89 | document.body.removeChild(el) 90 | } 91 | 92 | // extending a Custom Elements class. 93 | { 94 | const MyEl = Class().extends(native(HTMLElement), { 95 | constructor() { 96 | return super.constructor() 97 | }, 98 | connectedCallback() { 99 | this.connected = true 100 | }, 101 | }) 102 | 103 | const MyEl2 = Class().extends(MyEl, { 104 | constructor() { 105 | return super.constructor() 106 | }, 107 | connectedCallback() { 108 | super.connectedCallback() 109 | }, 110 | }) 111 | 112 | customElements.define('my-el2', MyEl2) 113 | const el = document.createElement('my-el2') 114 | 115 | expect(el instanceof MyEl2).toBe(true) 116 | 117 | document.body.appendChild(el) 118 | 119 | expect(el.connected).toBe(true) 120 | 121 | document.body.removeChild(el) 122 | } 123 | 124 | // When using `Reflect.construct`, use `this.constructor` in place of 125 | // `new.target` 126 | { 127 | const MyEl = Class().extends(native(HTMLElement), { 128 | constructor() { 129 | return Reflect.construct(super.constructor, [], this.constructor) 130 | }, 131 | connectedCallback() { 132 | this.connected = true 133 | }, 134 | }) 135 | 136 | const MyEl2 = Class().extends(MyEl, { 137 | constructor() { 138 | return Reflect.construct(super.constructor, [], this.constructor) 139 | }, 140 | connectedCallback() { 141 | super.connectedCallback() 142 | }, 143 | }) 144 | 145 | customElements.define('my-el3', MyEl2) 146 | const el = document.createElement('my-el3') 147 | 148 | expect(el instanceof MyEl2).toBe(true) 149 | 150 | document.body.appendChild(el) 151 | 152 | expect(el.connected).toBe(true) 153 | 154 | document.body.removeChild(el) 155 | } 156 | 157 | // if you provide your own classes, you can do it any way you want, 158 | // including using Reflect.construct with new.target 159 | { 160 | const MyEl = Class( 161 | ({Protected}) => 162 | class extends HTMLElement { 163 | constructor() { 164 | return Reflect.construct(HTMLElement, [], new.target) 165 | } 166 | connectedCallback() { 167 | Protected(this).connected = true 168 | } 169 | 170 | getProtectedMember() { 171 | return Protected(this).connected 172 | } 173 | 174 | // define initial protected values 175 | static protected() { 176 | return { 177 | connected: false, 178 | } 179 | } 180 | }, 181 | ) 182 | 183 | const MyEl2 = Class( 184 | ({Protected}) => 185 | class extends MyEl { 186 | constructor() { 187 | return Reflect.construct(MyEl, [], new.target) 188 | } 189 | 190 | connectedCallback() { 191 | super.connectedCallback() 192 | } 193 | }, 194 | ) 195 | 196 | customElements.define('my-el6', MyEl2) 197 | const el = document.createElement('my-el6') 198 | 199 | expect(el instanceof MyEl2).toBe(true) 200 | 201 | document.body.appendChild(el) 202 | 203 | expect(el.connected).toBe(undefined) 204 | expect(el.getProtectedMember()).toBe(true) 205 | 206 | document.body.removeChild(el) 207 | } 208 | }) 209 | }) 210 | -------------------------------------------------------------------------------- /src/tests/class-branding.test.js: -------------------------------------------------------------------------------- 1 | import {Class, InvalidAccessError, InvalidSuperAccessError} from '../Class.js' 2 | import Mixin from '../Mixin.js' 3 | 4 | const test = it 5 | 6 | describe('Class branding and positional privacy vs lexical privacy', () => { 7 | test(` 8 | Private/protected access works across instances of a class generated 9 | from multiple applications of a mixin passed the same base class. 10 | `, () => { 11 | // this test works because the following mixin applications are 12 | // memoized, so calling `Foo.mixin()` twice without supplying differeing 13 | // args causes the same class constructor to be returned both times. 14 | 15 | let count = 0 16 | 17 | const Foo = Mixin((Base = Class()) => { 18 | return Class('Foo').extends(Base, ({Super, Private}) => ({ 19 | constructor() { 20 | Super(this).constructor() 21 | Private(this).foo = ++count 22 | }, 23 | 24 | getPrivateFromOther(other) { 25 | return Private(other).foo 26 | }, 27 | })) 28 | // ^ a brand is not passed in here 29 | }) 30 | 31 | const A = Foo.mixin() 32 | const B = Foo.mixin() 33 | 34 | const a = new A() 35 | const b = new B() 36 | 37 | expect(a.getPrivateFromOther(b)).toBe(2) 38 | }) 39 | 40 | test(` 41 | If no brand is provided, private/protected access does NOT work across 42 | instances of a class generated from multiple applications of a mixin 43 | passed differing base classes. 44 | `, () => { 45 | // This test shows private access does not work. The following two calls 46 | // of `Foo.mixin()` are passed different base classes, so the return 47 | // values are two differeing class constructors. Not passing a brand 48 | // means that the private access will not be shared across these classes 49 | // (this is called "lexical privates" according to 50 | // https://github.com/tc39/proposal-class-fields/issues/60. 51 | 52 | let count = 0 53 | 54 | const Foo = Mixin((Base = Class()) => { 55 | return Class('Foo').extends(Base, ({Super, Private}) => ({ 56 | constructor() { 57 | Super(this).constructor() 58 | Private(this).foo = ++count 59 | }, 60 | 61 | getPrivateFromOther(other) { 62 | return Private(other).foo 63 | }, 64 | })) 65 | // ^ a brand is not passed in here 66 | }) 67 | 68 | const BaseA = Class() 69 | const BaseB = Class() 70 | 71 | const A = Foo.mixin(BaseA) 72 | const B = Foo.mixin(BaseB) 73 | 74 | const a = new A() 75 | const b = new B() 76 | 77 | // this won't work, the implementation will treat a and b as if they 78 | // were made from two unrelated class definitions 79 | expect(() => a.getPrivateFromOther(b)).toThrowError(InvalidAccessError) 80 | }) 81 | 82 | test(` 83 | If a brand is provided, private/protected access should work across 84 | instances of the same class generated from multiple applications of a 85 | mixin passed differing base classes. 86 | `, () => { 87 | // To make privacy work unlike in the previous example, we need to 88 | // define a brand for the classes generated by the mixins. The brand is 89 | // an object, and the Content of it doesn't matter (we could leave it 90 | // empty, it's just used internally as a WeakMap key). It tells the 91 | // Class implementation to share privacy across instances made from 92 | // classes that share the brand. This let's us achieve "positional 93 | // privacy" as described in 94 | // https://github.com/tc39/proposal-class-fields/issues/60 95 | const FooBrand = {brand: 'FooBrand'} 96 | 97 | let count = 0 98 | let proto = 0 99 | 100 | const Foo = Mixin((Base = Class()) => { 101 | return Class('Foo').extends( 102 | Base, 103 | ({Super, Private}) => ({ 104 | proto: ++proto, 105 | 106 | constructor() { 107 | Super(this).constructor() 108 | Private(this).foo = ++count 109 | }, 110 | 111 | getPrivateFromOther(other) { 112 | return Private(other).foo 113 | }, 114 | }), 115 | FooBrand, 116 | ) 117 | // ^ passing the brand enables behavior similar to "positional privacy" 118 | }) 119 | 120 | const BaseA = Class() 121 | const BaseB = Class() 122 | 123 | const A = Foo.mixin(BaseA) 124 | const B = Foo.mixin(BaseB) 125 | 126 | const a = new A() 127 | const b = new B() 128 | 129 | // although a and b were created from two different class constructors 130 | // due to the mixin calls, private access still works, thanks to the 131 | // brand which marks them as "from the same Foo class", similar to 132 | // privacy based on source position. 133 | expect(a.getPrivateFromOther(b)).toBe(2) 134 | }) 135 | 136 | test(`the Super helper should not work across instances of a branded class`, () => { 137 | const FooBrand = {brand: 'FooBrand'} 138 | 139 | const Foo = Mixin(Base => { 140 | return Class('Foo').extends( 141 | Base, 142 | ({Super, Private}) => ({ 143 | constructor() { 144 | Super(this).constructor() 145 | }, 146 | 147 | callSuperOnOther(other) { 148 | Super(other) 149 | }, 150 | }), 151 | FooBrand, 152 | ) 153 | // ^ passing the brand should not make Super behave like the access helpers 154 | }) 155 | 156 | const BaseA = Class() 157 | const BaseB = Class() 158 | 159 | const A = Foo.mixin(BaseA) 160 | const B = Foo.mixin(BaseB) 161 | 162 | const a = new A() 163 | const b = new B() 164 | 165 | expect(() => a.callSuperOnOther(b)).toThrowError(InvalidSuperAccessError) 166 | 167 | // but Super should work across instances of the exact same class. 168 | 169 | const BaseC = Class() 170 | 171 | // C and D are the exact same class because the mixin returns a cached 172 | // class when the same base class is passed 173 | const C = Foo.mixin(BaseC) 174 | const D = Foo.mixin(BaseC) 175 | 176 | const c = new C() 177 | const d = new D() 178 | 179 | expect(() => c.callSuperOnOther(d)).not.toThrowError(InvalidSuperAccessError) 180 | }) 181 | }) 182 | -------------------------------------------------------------------------------- /src/tests/configuration.test.js: -------------------------------------------------------------------------------- 1 | import {Class, createClassHelper, staticBlacklist} from '../Class.js' 2 | 3 | const test = it 4 | 5 | describe('configuration', () => { 6 | test('ensure that class prototype and static descriptors are like ES6 classes', () => { 7 | const Duck = Class(({Protected, Private}) => ({ 8 | constructor() {}, 9 | add() {}, 10 | get foo() {}, 11 | 12 | protected: { 13 | foo: 'foo', 14 | add() {}, 15 | get foo() {}, 16 | }, 17 | 18 | private: { 19 | foo: 'foo', 20 | add() {}, 21 | get foo() {}, 22 | }, 23 | 24 | static: { 25 | foo: 'foo', 26 | add() {}, 27 | set foo(v) {}, 28 | }, 29 | 30 | test() { 31 | checkDescriptors(Protected(this).__proto__) 32 | checkDescriptors(Private(this).__proto__) 33 | }, 34 | })) 35 | 36 | const protoDescriptor = Object.getOwnPropertyDescriptor(Duck, 'prototype') 37 | expect(!protoDescriptor.writable).toBeTruthy() 38 | expect(!protoDescriptor.enumerable).toBeTruthy() 39 | expect(!protoDescriptor.configurable).toBeTruthy() 40 | 41 | checkDescriptors(Duck) 42 | checkDescriptors(Duck.prototype) 43 | 44 | const duck = new Duck() 45 | duck.test() 46 | }) 47 | 48 | test('Show how to change class creation configuration', () => { 49 | // for example suppose we want static and prototype props/methods to be 50 | // enumerable, and the prototype to be writable. 51 | 52 | const Class = createClassHelper({ 53 | prototypeWritable: true, 54 | defaultClassDescriptor: { 55 | enumerable: true, 56 | configurable: false, 57 | }, 58 | }) 59 | 60 | const AwesomeThing = Class(({Protected, Private}) => ({ 61 | constructor() {}, 62 | add() {}, 63 | get foo() {}, 64 | 65 | protected: { 66 | foo: 'foo', 67 | add() {}, 68 | get foo() {}, 69 | }, 70 | 71 | private: { 72 | foo: 'foo', 73 | add() {}, 74 | get foo() {}, 75 | }, 76 | 77 | static: { 78 | foo: 'foo', 79 | add() {}, 80 | set foo(v) {}, 81 | }, 82 | 83 | test() { 84 | checkDescriptors(Protected(this).__proto__, true, false) 85 | checkDescriptors(Private(this).__proto__, true, false) 86 | }, 87 | })) 88 | 89 | const protoDescriptor = Object.getOwnPropertyDescriptor(AwesomeThing, 'prototype') 90 | expect(protoDescriptor.writable).toBeTruthy() 91 | expect(!protoDescriptor.enumerable).toBeTruthy() 92 | expect(!protoDescriptor.configurable).toBeTruthy() 93 | 94 | checkDescriptors(AwesomeThing, true, false) 95 | checkDescriptors(AwesomeThing.prototype, true, false) 96 | 97 | const thing = new AwesomeThing() 98 | thing.test() 99 | }) 100 | 101 | test('Show how to disable setting of descriptors', () => { 102 | // leaving them like ES5 classes (gives better performance while defining 103 | // classes too, if you don't need the stricter descriptors) 104 | 105 | const Class = createClassHelper({ 106 | setClassDescriptors: false, 107 | }) 108 | 109 | const PeanutBrittle = Class(({Protected, Private}) => ({ 110 | constructor() {}, 111 | add() {}, 112 | get foo() {}, 113 | 114 | protected: { 115 | foo: 'foo', 116 | add() {}, 117 | get foo() {}, 118 | }, 119 | 120 | private: { 121 | foo: 'foo', 122 | add() {}, 123 | get foo() {}, 124 | }, 125 | 126 | static: { 127 | foo: 'foo', 128 | add() {}, 129 | set foo(v) {}, 130 | }, 131 | 132 | test() { 133 | checkDescriptors(Protected(this).__proto__, true, true) 134 | checkDescriptors(Private(this).__proto__, true, true) 135 | }, 136 | })) 137 | 138 | const protoDescriptor = Object.getOwnPropertyDescriptor(PeanutBrittle, 'prototype') 139 | expect(protoDescriptor.writable).toBeTruthy() 140 | expect(!protoDescriptor.enumerable).toBeTruthy() 141 | expect(!protoDescriptor.configurable).toBeTruthy() 142 | 143 | checkDescriptors(PeanutBrittle, true, true) 144 | checkDescriptors(PeanutBrittle.prototype, true, true) 145 | 146 | const thing = new PeanutBrittle() 147 | thing.test() 148 | }) 149 | 150 | function checkDescriptors(obj, enumerable = false, configurable = true) { 151 | const useBlacklist = typeof obj === 'function' 152 | 153 | const descriptors = Object.getOwnPropertyDescriptors(obj) 154 | let descriptor 155 | 156 | expect(Object.keys(descriptors).length).toBeTruthy() 157 | 158 | for (const key in descriptors) { 159 | if (useBlacklist && staticBlacklist.includes(key)) continue 160 | 161 | descriptor = descriptors[key] 162 | 163 | if ('writable' in descriptor) expect(descriptor.writable).toBeTruthy() 164 | else expect('get' in descriptor).toBeTruthy() 165 | 166 | expect(descriptor.enumerable === enumerable).toBeTruthy() 167 | expect(descriptor.configurable === configurable).toBeTruthy() 168 | } 169 | } 170 | 171 | test('name classes natively (default is false)', () => { 172 | // without native naming 173 | { 174 | const Class = createClassHelper({ 175 | nativeNaming: false, // default 176 | }) 177 | 178 | // anonymous: 179 | const Something = Class() 180 | expect(Something.name === '').toBeTruthy() 181 | 182 | // named: 183 | const OtherThing = Class('OtherThing') 184 | expect(OtherThing.name === 'OtherThing').toBeTruthy() 185 | 186 | expect(!OtherThing.toString().includes('OtherThing')).toBeTruthy() 187 | 188 | // make sure works with non-simple classes (because different code path) 189 | const AwesomeThing = Class({method() {}}) 190 | expect(AwesomeThing.name).toBe('') 191 | const AwesomeThing2 = Class('AwesomeThing2', {method() {}}) 192 | expect(AwesomeThing2.name).toBe('AwesomeThing2') 193 | expect(!AwesomeThing2.toString().includes('AwesomeThing2')).toBeTruthy() 194 | } 195 | 196 | // with native naming 197 | { 198 | // this config causes functions to be created using naming that is 199 | // native to the engine, by doing something like this: 200 | // new Function(` return function ${ className }() { ... } `) 201 | const Class = createClassHelper({ 202 | nativeNaming: true, 203 | }) 204 | 205 | // anonymous: 206 | const AnotherThing = Class() 207 | expect(AnotherThing.name === '').toBeTruthy() 208 | 209 | // named: 210 | const YetAnotherThing = Class('YetAnotherThing') 211 | expect(YetAnotherThing.name === 'YetAnotherThing').toBeTruthy() 212 | 213 | // here's the difference 214 | expect(YetAnotherThing.toString().includes('YetAnotherThing')).toBeTruthy() 215 | 216 | // make sure works with non-simple classes (because different code path) 217 | const AwesomeThing = Class({method() {}}) 218 | expect(AwesomeThing.name).toBe('') 219 | const AwesomeThing2 = Class('AwesomeThing2', {method() {}}) 220 | expect(AwesomeThing2.name).toBe('AwesomeThing2') 221 | expect(AwesomeThing2.toString().includes('AwesomeThing2')).toBeTruthy() 222 | } 223 | }) 224 | }) 225 | -------------------------------------------------------------------------------- /src/Mixin.test.ts: -------------------------------------------------------------------------------- 1 | // TODO no @ts-ignore comments 2 | 3 | import Mixin, {HasInstance, type MixinResult} from './Mixin.js' 4 | import instanceOf from './instanceOf.js' 5 | import {Constructor} from './Constructor.js' 6 | 7 | // TODO move type def to @lume/cli, map @types/jest's `expect` type into the 8 | // global env. 9 | declare global { 10 | function expect(...args: any[]): any 11 | } 12 | 13 | describe('Mixin', () => { 14 | it('Mixin returns a Function', () => { 15 | // const Foo = Mixin(Base => class Foo extends Constructor(Base || Object) {}) 16 | function FooMixin(Base: T) { 17 | class Foo extends Constructor(Base) {} 18 | return Foo as MixinResult 19 | } 20 | const Foo = Mixin(FooMixin) 21 | class Bar {} 22 | const Baz = Foo.mixin(Bar) 23 | const Lorem = Foo.mixin(Bar) 24 | 25 | expect(typeof Foo).toBe('function') 26 | expect(typeof Foo.mixin).toBe('function') 27 | expect(typeof Baz).toBe('function') 28 | expect(typeof Lorem).toBe('function') 29 | }) 30 | 31 | it('Mixin applications are cached', () => { 32 | // const Foo = Mixin(Base => class Foo extends Constructor(Base || Object) {}) 33 | function FooMixin(Base: T) { 34 | class Foo extends Constructor(Base) {} 35 | return Foo as MixinResult 36 | } 37 | const Foo = Mixin(FooMixin) 38 | class Bar {} 39 | const Baz = Foo.mixin(Bar) 40 | const Lorem = Foo.mixin(Bar) 41 | 42 | // caching of the same mixin application 43 | expect(Baz).toBe(Lorem) 44 | }) 45 | 46 | it('instanceof works with multiple classes generated from the same Mixin', () => { 47 | // const Foo = Mixin(Base => class Foo extends Constructor(Base || Object) {}) 48 | function FooMixin(Base: T) { 49 | class Foo extends Constructor(Base) {} 50 | return Foo as MixinResult 51 | } 52 | const Foo = Mixin(FooMixin) 53 | class Bar {} 54 | const Baz = Foo.mixin(Bar) 55 | const Lorem = Foo.mixin(Bar) 56 | 57 | const baz = new Baz() 58 | 59 | expect(baz instanceof Foo).toBe(true) 60 | expect(baz instanceof Bar).toBe(true) 61 | expect(baz instanceof Baz).toBe(true) 62 | expect(baz instanceof Lorem).toBe(true) 63 | 64 | expect(instanceOf(baz, Foo)).toBe(true) 65 | expect(instanceOf(baz, Bar)).toBe(true) 66 | expect(instanceOf(baz, Baz)).toBe(true) 67 | expect(instanceOf(baz, Lorem)).toBe(true) 68 | }) 69 | 70 | it('HasInstance delegates to super Symbol.hasInstance method, so regular instanceof works', () => { 71 | // const Foo = Mixin(Base => class Foo extends Constructor(Base || Object) {}) 72 | function FooMixin(Base: T) { 73 | class Foo extends Constructor(Base) {} 74 | return Foo as MixinResult 75 | } 76 | const Foo = Mixin(FooMixin) 77 | class Bar {} 78 | const Baz = Foo.mixin(Bar) 79 | 80 | expect({} instanceof Baz).toBe(false) 81 | 82 | class Thing extends Baz {} 83 | 84 | expect(new Thing() instanceof Thing).toBe(true) 85 | }) 86 | 87 | it('When Symbol is supported, instanceof works', () => { 88 | // const Ipsum = Mixin(Base => class Ipsum extends Constructor(Base || Object) {}) 89 | function IpsumMixin(Base: T) { 90 | class Ipsum extends Constructor(Base) {} 91 | return Ipsum as MixinResult 92 | } 93 | const Ipsum = Mixin(IpsumMixin) 94 | class Blah {} 95 | const One = Ipsum.mixin(Blah) 96 | 97 | const one = new One() 98 | 99 | expect(one instanceof One).toBe(true) 100 | 101 | // there's two versions of Ipsum in play, the original one, and the one 102 | // created when making `One`, but instanceof checks still work: 103 | expect(one instanceof Ipsum).toBe(true) 104 | }) 105 | 106 | it('When Symbol is not supported, instanceof does not work', () => { 107 | function test() { 108 | // const Ipsum = Mixin(Base => class Ipsum extends Constructor(Base || Object) {}) 109 | function IpsumMixin(Base: T) { 110 | class Ipsum extends Constructor(Base) {} 111 | return Ipsum as MixinResult 112 | } 113 | const Ipsum = Mixin(IpsumMixin) 114 | class Blah {} 115 | const One = Ipsum.mixin(Blah) 116 | 117 | const one = new One() 118 | 119 | expect(one instanceof One).toBe(true) 120 | 121 | // Without Symbol.hasInstance, the internal trick doesn't work, so 122 | // instanceof won't be useful like we'd like it to be: 123 | expect(one instanceof Ipsum).toBe(false) 124 | } 125 | 126 | const originalSymbol = Symbol 127 | 128 | // Sometimes Symbol() is polyfilled in a way that it generates a random 129 | // regular property key. 130 | Symbol = (() => Math.random()) as any 131 | 132 | test() 133 | 134 | // Sometimes Symbol is not defined in the environment. 135 | Symbol = void 0 as any 136 | 137 | test() 138 | 139 | Symbol = originalSymbol 140 | }) 141 | 142 | it('if a class already has its own Symbol.hasInstance method, we do not override it', () => { 143 | function fn() {} 144 | 145 | let FooMixin = function FooMixin(Base: T) { 146 | class Foo extends Constructor(Base || Object) {} 147 | Object.defineProperty(Foo, Symbol.hasInstance, {value: fn}) 148 | return Foo as MixinResult 149 | } 150 | 151 | // @ts-ignore TS v4 introduced a type error 152 | FooMixin = HasInstance(FooMixin) 153 | 154 | const Foo = FooMixin(class {}) 155 | 156 | expect(Foo[Symbol.hasInstance]).toBe(fn) 157 | 158 | let BarMixin = function BarMixin(Base: T) { 159 | class Bar extends Constructor(Base || Object) {} 160 | return Bar as MixinResult 161 | } 162 | 163 | // @ts-ignore TS v4 introduced a type error 164 | BarMixin = HasInstance(BarMixin) 165 | 166 | const Bar = BarMixin(class {}) 167 | 168 | expect(Bar[Symbol.hasInstance]).not.toBe(fn) 169 | }) 170 | 171 | it('configuring a default base class', () => { 172 | // const Foo = Mixin(Base => class Foo extends Constructor(Base || Object) {}, Map) 173 | function FooMixin(Base: T) { 174 | class Foo extends Constructor(Base) {} 175 | return Foo as MixinResult 176 | } 177 | const Foo = Mixin(FooMixin, Map) 178 | const Bar = class Bar extends Foo {} 179 | const bar = new Bar() 180 | // @ts-ignore 181 | const Baz = class Baz extends Foo.mixin(WeakMap) {} 182 | const baz = new Baz() 183 | 184 | expect(bar instanceof Map).toBe(true) 185 | 186 | expect(baz instanceof Map).toBe(false) 187 | expect(baz instanceof WeakMap).toBe(true) 188 | }) 189 | 190 | it('check there are no duplicate applications of a mixin in a class hierarchy', () => { 191 | function FooMixin(Base: T) { 192 | class Foo extends Constructor(Base) {} 193 | return Foo as MixinResult 194 | } 195 | const Foo = Mixin(FooMixin, Map) 196 | class Bar extends Foo {} 197 | 198 | // because Bar already has Foo 199 | expect(Foo.mixin(Bar)).toBe(Bar) 200 | }) 201 | }) 202 | -------------------------------------------------------------------------------- /src/tests/syntaxes.test.js: -------------------------------------------------------------------------------- 1 | // various forms of writing classes ("syntaxes") 2 | 3 | import {Class} from '../Class.js' 4 | import {native} from '../native.js' 5 | 6 | const test = it 7 | 8 | describe('various forms of writing classes', () => { 9 | test('object literal', () => { 10 | const Foo = Class({ 11 | constructor() { 12 | this.bar = 'bar' 13 | }, 14 | foo() { 15 | expect(this.bar === 'bar').toBeTruthy() 16 | }, 17 | }) 18 | 19 | const f = new Foo() 20 | expect(f instanceof Foo).toBeTruthy() 21 | f.foo() 22 | }) 23 | 24 | test('definer function (arrow function), returning an object literal', () => { 25 | const Foo = Class(({Super}) => ({ 26 | constructor() { 27 | this.bar = 'bar' 28 | }, 29 | foo() { 30 | expect(Super(this).hasOwnProperty('bar')).toBe(true) 31 | expect(this.bar === 'bar').toBeTruthy() 32 | }, 33 | })) 34 | 35 | const f = new Foo() 36 | expect(f instanceof Foo).toBeTruthy() 37 | f.foo() 38 | }) 39 | 40 | test('definer function (non-arrow), returning an object literal', () => { 41 | const Foo = Class(function ({Super}) { 42 | return { 43 | constructor() { 44 | this.bar = 'bar' 45 | }, 46 | foo() { 47 | expect(Super(this).hasOwnProperty('bar')).toBe(true) 48 | expect(this.bar === 'bar').toBeTruthy() 49 | }, 50 | } 51 | }) 52 | 53 | const f = new Foo() 54 | expect(f instanceof Foo).toBeTruthy() 55 | f.foo() 56 | }) 57 | 58 | test('definer function (arrow function), setting ES5-like prototype assignment', () => { 59 | const Foo = Class(({Super, Public}) => { 60 | Public.prototype.constructor = function () { 61 | this.bar = 'bar' 62 | } 63 | Public.prototype.foo = function () { 64 | expect(Super(this).hasOwnProperty('bar')).toBe(true) 65 | expect(this.bar === 'bar').toBeTruthy() 66 | } 67 | }) 68 | 69 | const f = new Foo() 70 | expect(f instanceof Foo).toBeTruthy() 71 | f.foo() 72 | }) 73 | 74 | test('wrap a native class', () => { 75 | const Foo = Class( 76 | () => 77 | class { 78 | constructor() { 79 | this.bar = 'bar' 80 | } 81 | foo() { 82 | expect(this.bar === 'bar').toBeTruthy() 83 | } 84 | }, 85 | ) 86 | 87 | const f = new Foo() 88 | expect(f instanceof Foo).toBeTruthy() 89 | f.foo() 90 | }) 91 | 92 | test('wrap an ES5 class', () => { 93 | const Foo = Class(() => { 94 | function Foo() { 95 | this.bar = 'bar' 96 | } 97 | 98 | Foo.prototype = { 99 | constructor: Foo, 100 | foo() { 101 | expect(this.bar === 'bar').toBeTruthy() 102 | }, 103 | } 104 | 105 | return Foo 106 | }) 107 | 108 | const f = new Foo() 109 | expect(f instanceof Foo).toBeTruthy() 110 | f.foo() 111 | }) 112 | 113 | test('object literal with access helpers on each access definition', () => { 114 | const Foo = Class({ 115 | public: (Protected, Private) => ({ 116 | constructor() { 117 | this.bar = 'bar' 118 | }, 119 | foo() { 120 | expect(this.bar === 'bar').toBeTruthy() 121 | expect(Protected(this).foo() === 'barbar3').toBeTruthy() 122 | expect(Private(this).foo() === 'barbar2').toBeTruthy() 123 | return 'it works' 124 | }, 125 | }), 126 | protected: (Public, Private) => ({ 127 | bar: 'bar2', 128 | foo() { 129 | return Public(this).bar + Private(this).bar 130 | }, 131 | }), 132 | private: (Public, Protected) => ({ 133 | bar: 'bar3', 134 | foo() { 135 | return Public(this).bar + Protected(this).bar 136 | }, 137 | }), 138 | }) 139 | 140 | const f = new Foo() 141 | expect(f instanceof Foo).toBeTruthy() 142 | expect(f.foo() === 'it works').toBeTruthy() 143 | 144 | const Bar = Foo.subclass({ 145 | public: Protected => ({ 146 | test() { 147 | return Protected(this).test() 148 | }, 149 | }), 150 | protected: ({Super, Public}) => ({ 151 | test() { 152 | return Super(Public(this)).foo() 153 | }, 154 | }), 155 | }) 156 | 157 | const b = new Bar() 158 | expect(b instanceof Bar).toBeTruthy() 159 | expect(b.foo() === 'it works').toBeTruthy() 160 | }) 161 | 162 | test('definer function and ES5-like prototype assignment', () => { 163 | const Foo = Class(({Protected, Private, Public}) => { 164 | Public.prototype.constructor = function () { 165 | this.bar = 'bar' 166 | } 167 | 168 | Public.prototype.foo = function () { 169 | expect(this.bar === 'bar').toBeTruthy() 170 | expect(Protected(this).foo() === 'barbar3').toBeTruthy() 171 | expect(Private(this).foo() === 'barbar2').toBeTruthy() 172 | return 'it works' 173 | } 174 | 175 | Protected.prototype.bar = 'bar2' 176 | 177 | Protected.prototype.foo = function () { 178 | return Public(this).bar + Private(this).bar 179 | } 180 | 181 | Private.prototype.bar = 'bar3' 182 | 183 | Private.prototype.foo = function () { 184 | return Public(this).bar + Protected(this).bar 185 | } 186 | }) 187 | 188 | const f = new Foo() 189 | expect(f instanceof Foo).toBeTruthy() 190 | expect(f.foo() === 'it works').toBeTruthy() 191 | 192 | const Bar = Foo.subclass(({Public, Protected, Super}) => { 193 | Public.prototype.test = function () { 194 | return Protected(this).test() 195 | } 196 | 197 | Protected.prototype.test = function () { 198 | return Super(Public(this)).foo() 199 | } 200 | }) 201 | 202 | const b = new Bar() 203 | expect(b instanceof Bar).toBeTruthy() 204 | expect(b.foo() === 'it works').toBeTruthy() 205 | }) 206 | 207 | test('definer function and ES5-like prototype object literals', () => { 208 | const Foo = Class(({Protected, Private, Public}) => { 209 | Public.prototype = { 210 | constructor() { 211 | this.bar = 'bar' 212 | }, 213 | 214 | foo() { 215 | expect(this.bar === 'bar').toBeTruthy() 216 | expect(Protected(this).foo() === 'barbar3').toBeTruthy() 217 | expect(Private(this).foo() === 'barbar2').toBeTruthy() 218 | return 'it works' 219 | }, 220 | } 221 | 222 | Protected.prototype = { 223 | bar: 'bar2', 224 | 225 | foo() { 226 | return Public(this).bar + Private(this).bar 227 | }, 228 | } 229 | 230 | Private.prototype = { 231 | bar: 'bar3', 232 | 233 | foo() { 234 | return Public(this).bar + Protected(this).bar 235 | }, 236 | } 237 | }) 238 | 239 | const f = new Foo() 240 | expect(f instanceof Foo).toBeTruthy() 241 | expect(f.foo() === 'it works').toBeTruthy() 242 | 243 | const Bar = Foo.subclass(({Public, Protected, Super}) => { 244 | Public.prototype = { 245 | test() { 246 | return Protected(this).test() 247 | }, 248 | } 249 | 250 | Protected.prototype = { 251 | test() { 252 | return Super(Public(this)).foo() 253 | }, 254 | } 255 | }) 256 | 257 | const b = new Bar() 258 | expect(b instanceof Bar).toBeTruthy() 259 | expect(b.foo() === 'it works').toBeTruthy() 260 | }) 261 | 262 | test('different ways to make a subclass', () => { 263 | let Foo = Class() 264 | 265 | let Bar = Class().extends(Foo, { 266 | method() {}, 267 | }) 268 | let bar = new Bar() 269 | 270 | expect(bar instanceof Foo).toBeTruthy() 271 | expect(bar instanceof Bar).toBeTruthy() 272 | expect(typeof bar.method).toBe('function') 273 | 274 | Bar = Class({ 275 | method() {}, 276 | }).extends(Foo) 277 | bar = new Bar() 278 | 279 | expect(bar instanceof Foo).toBeTruthy() 280 | expect(bar instanceof Bar).toBeTruthy() 281 | expect(typeof bar.method).toBe('function') 282 | 283 | Bar = Foo.subclass({ 284 | method() {}, 285 | }) 286 | bar = new Bar() 287 | 288 | expect(bar instanceof Foo).toBeTruthy() 289 | expect(bar instanceof Bar).toBeTruthy() 290 | expect(typeof bar.method).toBe('function') 291 | 292 | // TODO these doesn't work yet, but they should so that it is easy to work with existing code bases { 293 | 294 | //Foo = class {} 295 | //Foo.subclass = Class 296 | //Bar = Foo.subclass({ 297 | //method() {} 298 | //}) 299 | //bar = new Bar 300 | 301 | //expect( bar instanceof Foo ).toBeTruthy() 302 | //expect( bar instanceof Bar ).toBeTruthy() 303 | //expect( typeof bar.method ).toBe( 'function' ) 304 | 305 | //Foo = native( class {} ) 306 | //Foo.subclass = Class 307 | //Bar = Foo.subclass({ 308 | //method() {} 309 | //}) 310 | //bar = new Bar 311 | 312 | //expect( bar instanceof Foo ).toBeTruthy() 313 | //expect( bar instanceof Bar ).toBeTruthy() 314 | //expect( typeof bar.method ).toBe( 'function' ) 315 | 316 | //Foo = Class( () => class {} ) 317 | //Foo.subclass = Class 318 | //Bar = Foo.subclass({ 319 | //method() {} 320 | //}) 321 | //bar = new Bar 322 | 323 | //expect( bar instanceof Foo ).toBeTruthy() 324 | //expect( bar instanceof Bar ).toBeTruthy() 325 | //expect( typeof bar.method ).toBe( 'function' ) 326 | 327 | // } 328 | }) 329 | }) 330 | -------------------------------------------------------------------------------- /dist/multiple.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"multiple.js","sourceRoot":"","sources":["../src/multiple.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/tests/Counter.d.ts: -------------------------------------------------------------------------------- 1 | export const Counter: { 2 | extends(base: TBase, members: (helpers: { 3 | Super: (self: T_1) => TBase extends new (...a: infer A extends any[]) => infer I ? { 4 | constructor: (...a: A) => I; 5 | } & InstanceType : never; 6 | Public: (self: T_1) => Omit; 7 | Protected: (self: T_1) => T_1 extends { 8 | __: { 9 | protected: infer TProtected; 10 | }; 11 | } ? TProtected : never; 12 | Private: (self: T_1) => T_1 extends { 13 | __: { 14 | private: infer TPrivate; 15 | }; 16 | } ? TPrivate : never; 17 | }) => T & Partial> & ThisType & (TBase extends { 18 | __: infer TProtected; 19 | } ? TProtected : {}), "static" | "private" | "protected"> & { 20 | __: Extract | Extract, "static" | "private" | "protected"> | Extract extends infer T_2 extends keyof (T & InstanceType & (TBase extends { 23 | __: infer TProtected; 24 | } ? TProtected : {})) ? { [P_1 in T_2]: (T & InstanceType & (TBase extends { 25 | __: infer TProtected; 26 | } ? TProtected : {}))[P_1]; } : never; 27 | } extends infer T_1 ? { [P in keyof T_1]: (Omit & (TBase extends { 28 | __: infer TProtected; 29 | } ? TProtected : {}), "static" | "private" | "protected"> & { 30 | __: Extract | Extract, "static" | "private" | "protected"> | Extract extends infer T_2 extends keyof (T & InstanceType & (TBase extends { 33 | __: infer TProtected; 34 | } ? TProtected : {})) ? { [P_1 in T_2]: (T & InstanceType & (TBase extends { 35 | __: infer TProtected; 36 | } ? TProtected : {}))[P_1]; } : never; 37 | })[P]; } : never>, brand?: object): T extends { 38 | constructor: infer _TCtor; 39 | } ? ((T extends infer T_1 ? T_1 extends T ? T_1 extends { 40 | constructor: infer TCtor; 41 | } ? TCtor : () => void : never : never) extends infer T_3 ? T_3 extends (T extends infer T_1 ? T_1 extends T ? T_1 extends { 42 | constructor: infer TCtor; 43 | } ? TCtor : () => void : never : never) ? T_3 extends (...a: infer A) => void ? new (...a: A) => InstanceType & (Exclude extends infer T_2 extends keyof T ? { [P in T_2]: T[P]; } : never) extends infer T_4 ? { [P_1 in keyof T_4]: (InstanceType & (Exclude extends infer T_2 extends keyof T ? { [P in T_2]: T[P]; } : never))[P_1]; } : never : never : never : never) & import("../types.js").Id<((T extends infer T_5 ? T_5 extends T ? T_5 extends { 44 | static: infer TStatic; 45 | } ? TStatic : {} : never : never) & { 46 | __: { 47 | protected: T extends infer T_6 ? T_6 extends T ? T_6 extends { 48 | protected: infer TProtected; 49 | } ? TProtected : {} : never : never; 50 | }; 51 | } extends infer T_4 ? { [P_1 in keyof T_4]: ((T extends infer T_5 ? T_5 extends T ? T_5 extends { 52 | static: infer TStatic; 53 | } ? TStatic : {} : never : never) & { 54 | __: { 55 | protected: T extends infer T_6 ? T_6 extends T ? T_6 extends { 56 | protected: infer TProtected; 57 | } ? TProtected : {} : never : never; 58 | }; 59 | })[P_1]; } : never) & Pick> : (TBase extends new (...a: infer A) => unknown ? new (...a: A) => InstanceType extends infer T_1 ? { [P in keyof T_1]: InstanceType[P]; } : never : never) & import("../types.js").Id<((T extends { 60 | static: infer TStatic; 61 | } ? TStatic : {}) & { 62 | __: { 63 | protected: T extends { 64 | protected: infer TProtected; 65 | } ? TProtected : {}; 66 | }; 67 | } extends infer T_1 ? { [P in keyof T_1]: ((T extends { 68 | static: infer TStatic; 69 | } ? TStatic : {}) & { 70 | __: { 71 | protected: T extends { 72 | protected: infer TProtected; 73 | } ? TProtected : {}; 74 | }; 75 | })[P]; } : never) & Pick>; 76 | }; 77 | export const Incrementor: { 78 | extends(base: TBase, members: (helpers: { 79 | Super: (self: T_1) => TBase extends new (...a: infer A extends any[]) => infer I ? { 80 | constructor: (...a: A) => I; 81 | } & InstanceType : never; 82 | Public: (self: T_1) => Omit; 83 | Protected: (self: T_1) => T_1 extends { 84 | __: { 85 | protected: infer TProtected; 86 | }; 87 | } ? TProtected : never; 88 | Private: (self: T_1) => T_1 extends { 89 | __: { 90 | private: infer TPrivate; 91 | }; 92 | } ? TPrivate : never; 93 | }) => T & Partial> & ThisType & (TBase extends { 94 | __: infer TProtected; 95 | } ? TProtected : {}), "static" | "private" | "protected"> & { 96 | __: Extract | Extract, "static" | "private" | "protected"> | Extract extends infer T_2 extends keyof (T & InstanceType & (TBase extends { 99 | __: infer TProtected; 100 | } ? TProtected : {})) ? { [P_1 in T_2]: (T & InstanceType & (TBase extends { 101 | __: infer TProtected; 102 | } ? TProtected : {}))[P_1]; } : never; 103 | } extends infer T_1 ? { [P in keyof T_1]: (Omit & (TBase extends { 104 | __: infer TProtected; 105 | } ? TProtected : {}), "static" | "private" | "protected"> & { 106 | __: Extract | Extract, "static" | "private" | "protected"> | Extract extends infer T_2 extends keyof (T & InstanceType & (TBase extends { 109 | __: infer TProtected; 110 | } ? TProtected : {})) ? { [P_1 in T_2]: (T & InstanceType & (TBase extends { 111 | __: infer TProtected; 112 | } ? TProtected : {}))[P_1]; } : never; 113 | })[P]; } : never>, brand?: object): T extends { 114 | constructor: infer _TCtor; 115 | } ? ((T extends infer T_1 ? T_1 extends T ? T_1 extends { 116 | constructor: infer TCtor; 117 | } ? TCtor : () => void : never : never) extends infer T_3 ? T_3 extends (T extends infer T_1 ? T_1 extends T ? T_1 extends { 118 | constructor: infer TCtor; 119 | } ? TCtor : () => void : never : never) ? T_3 extends (...a: infer A) => void ? new (...a: A) => InstanceType & (Exclude extends infer T_2 extends keyof T ? { [P in T_2]: T[P]; } : never) extends infer T_4 ? { [P_1 in keyof T_4]: (InstanceType & (Exclude extends infer T_2 extends keyof T ? { [P in T_2]: T[P]; } : never))[P_1]; } : never : never : never : never) & import("../types.js").Id<((T extends infer T_5 ? T_5 extends T ? T_5 extends { 120 | static: infer TStatic; 121 | } ? TStatic : {} : never : never) & { 122 | __: { 123 | protected: T extends infer T_6 ? T_6 extends T ? T_6 extends { 124 | protected: infer TProtected; 125 | } ? TProtected : {} : never : never; 126 | }; 127 | } extends infer T_4 ? { [P_1 in keyof T_4]: ((T extends infer T_5 ? T_5 extends T ? T_5 extends { 128 | static: infer TStatic; 129 | } ? TStatic : {} : never : never) & { 130 | __: { 131 | protected: T extends infer T_6 ? T_6 extends T ? T_6 extends { 132 | protected: infer TProtected; 133 | } ? TProtected : {} : never : never; 134 | }; 135 | })[P_1]; } : never) & Pick> : (TBase extends new (...a: infer A) => unknown ? new (...a: A) => InstanceType extends infer T_1 ? { [P in keyof T_1]: InstanceType[P]; } : never : never) & import("../types.js").Id<((T extends { 136 | static: infer TStatic; 137 | } ? TStatic : {}) & { 138 | __: { 139 | protected: T extends { 140 | protected: infer TProtected; 141 | } ? TProtected : {}; 142 | }; 143 | } extends infer T_1 ? { [P in keyof T_1]: ((T extends { 144 | static: infer TStatic; 145 | } ? TStatic : {}) & { 146 | __: { 147 | protected: T extends { 148 | protected: infer TProtected; 149 | } ? TProtected : {}; 150 | }; 151 | })[P]; } : never) & Pick>; 152 | }; 153 | //# sourceMappingURL=Counter.d.ts.map -------------------------------------------------------------------------------- /src/native.ts: -------------------------------------------------------------------------------- 1 | // borrowed from (and slightly modified) https://github.com/Mr0grog/newless 2 | // The newless license is BSD 3: 3 | 4 | // TODO no any types 5 | 6 | /* 7 | * Copyright (c) 2013-2016, Rob Brackett 8 | * Copyright (c) 2018, Joseph Orbegoso Pea 9 | * All rights reserved. 10 | * 11 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 12 | * 13 | * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 14 | * 15 | * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 16 | * 17 | * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 18 | * 19 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 20 | */ 21 | 22 | import {getFunctionBody, setDescriptor} from './utils.js' 23 | import {Constructor} from './Constructor.js' 24 | 25 | export {newless as native} 26 | 27 | export default newless 28 | 29 | var supportsSpread = isSyntaxSupported('Object(...[{}])') 30 | var supportsClass = isSyntaxSupported('class Test {}') 31 | var supportsNewTarget = isSyntaxSupported('new.target') 32 | 33 | // Used to track the original wrapped constructor on a newless instance 34 | var TRUE_CONSTRUCTOR = Symbol ? Symbol('trueConstructor') : '__newlessTrueConstructor__' 35 | 36 | var setPrototype = 37 | Object.setPrototypeOf || 38 | function setPrototypeOf(object, newPrototype) { 39 | object.__proto__ = newPrototype 40 | } 41 | 42 | // Polyfill for Reflect.construct 43 | var construct = 44 | (Reflect && Reflect.construct) || 45 | (function () { 46 | if (supportsClass) { 47 | return Function( 48 | 'constructor, args, target', 49 | ` 50 | 'use strict'; 51 | 52 | if (arguments.length === 3 && typeof target !== 'function') 53 | throw new TypeError(target + ' is not a constructor'); 54 | 55 | target = target || constructor; 56 | 57 | // extend target so the right prototype is constructed (or nearly the 58 | // right one; ideally we'd do instantiator.prototype = target.prototype, 59 | // but a class's prototype property is not writable) 60 | class instantiator extends target {}; 61 | // but ensure the *logic* is 'constructor' for ES2015-compliant engines 62 | Object.setPrototypeOf(instantiator, constructor); 63 | // ...and for Safari 9 64 | instantiator.prototype.constructor = constructor; 65 | 66 | // The spread operator is *dramatically faster, so use it if we can: 67 | // http://jsperf.com/new-via-spread-vs-dynamic-function/4 68 | ${ 69 | supportsSpread 70 | ? ` 71 | 72 | var value = new instantiator(...([].slice.call(args))); 73 | 74 | ` 75 | : ` 76 | 77 | // otherwise, create a dynamic function in order to use 'new' 78 | // Note using 'function.bind' would be simpler, but is much slower: 79 | // http://jsperf.com/new-operator-with-dynamic-function-vs-bind 80 | var argList = ''; 81 | for (var i = 0, len = args.length; i < len; i++) { 82 | if (i > 0) argList += ','; 83 | argList += 'args[' + i + ']'; 84 | } 85 | var constructCall = Function('constructor, args', 86 | 'return new constructor( ' + argList + ' );' 87 | ); 88 | var value = constructCall(constructor, args); 89 | 90 | args = Array.prototype.slice.call(args); 91 | args = [null].concat(args); 92 | var value = new constructor.bind.apply(constructor, args); 93 | 94 | ` 95 | } 96 | 97 | // fix up the prototype so it matches the intended one, not one who's 98 | // prototype is the intended one :P 99 | Object.setPrototypeOf(value, target.prototype); 100 | return value; 101 | `, 102 | ) 103 | 104 | //return Function("constructor, args, newTarget", ` 105 | // 'use strict'; 106 | 107 | // if (arguments.length === 3 && typeof newTarget === undefined) 108 | // throw new TypeError('undefined is not a constructor'); 109 | 110 | // newTarget = newTarget || constructor; 111 | 112 | // ${ supportsSpread ? ` 113 | 114 | // var value = new constructor(...([].slice.call(args))); 115 | 116 | // `:` 117 | 118 | // args = Array.prototype.slice.call(args); 119 | // args = [null].concat(args); 120 | // var value = new constructor.bind.apply(constructor, args); 121 | 122 | // `} 123 | 124 | // Object.setPrototypeOf(value, newTarget.prototype); 125 | // return value; 126 | //`); 127 | } else { 128 | var instantiator = function () {} as any 129 | return function construct(constructor: any, args: any, target: any) { 130 | if (arguments.length === 3 && typeof target !== 'function') 131 | throw new TypeError(target + ' is not a constructor') 132 | instantiator.prototype = (target || constructor).prototype 133 | var instance = new instantiator() 134 | var value = constructor.apply(instance, args) 135 | if (typeof value === 'object' && value) { 136 | // we can do better if __proto__ is available (in some ES5 environments) 137 | value.__proto__ = (target || constructor).prototype 138 | return value 139 | } 140 | return instance 141 | } 142 | } 143 | })() 144 | 145 | // ES2015 class methods are non-enumerable; we need a helper for copying them. 146 | var SKIP_PROPERTIES: (string | symbol)[] = ['arguments', 'caller', 'length', 'name', 'prototype'] 147 | function copyProperties(source: any, destination: any) { 148 | if (Object.getOwnPropertyNames && Object.defineProperty) { 149 | var properties: (string | symbol)[] = Object.getOwnPropertyNames(source) 150 | if (Object.getOwnPropertySymbols) { 151 | properties = properties.concat(Object.getOwnPropertySymbols(source)) 152 | } 153 | for (var i = properties.length - 1; i >= 0; i--) { 154 | if (SKIP_PROPERTIES.indexOf(properties[i]!) === -1) { 155 | Object.defineProperty( 156 | destination, 157 | properties[i]!, 158 | Object.getOwnPropertyDescriptor(source, properties[i]!)!, 159 | ) 160 | } 161 | } 162 | } else { 163 | for (var property in source) { 164 | destination[property] = source[property] 165 | } 166 | } 167 | } 168 | 169 | type FuncLikeCtor = { 170 | (): T 171 | new (): T 172 | } & S 173 | 174 | function newless(constructor: T): FuncLikeCtor, T> { 175 | var name = constructor.name 176 | 177 | // V8 and newer versions of JSCore return the full class declaration from 178 | // `toString()`, which lets us be a little smarter and more performant 179 | // about what to do, since we know we are dealing with a "class". Note, 180 | // however, not all engines do this. This could be false and the constructor 181 | // might still use class syntax. 182 | var usesClassSyntax = constructor.toString().substr(0, 5) === 'class' 183 | 184 | var requiresNew = usesClassSyntax ? true : null 185 | 186 | var newlessConstructor: CtorWithLength = (() => 187 | function (this: any) { 188 | // If called with an already valid 'this', preserve that 'this' value 189 | // in the super-type's constructor whenever possible. With function 190 | // constructors (as opposed to class constructors), it's possible to 191 | // alter the instance before calling the super constructor--so it's 192 | // important to preserve that instance if at all possible. 193 | if (!requiresNew && this instanceof newlessConstructor) { 194 | // requiresNew = 'false' indicates we know the 'new' operator isn't 195 | // necessary for this constructor, but 'null' indicates uncertainty, 196 | // so the call needs to handle potential errors the first time in 197 | // order to determine whether 'new' is definitely required. 198 | if (requiresNew === false) { 199 | const returnValue = constructor.apply(this, arguments as any) 200 | return (typeof returnValue === 'object' && returnValue) || this 201 | } 202 | try { 203 | requiresNew = false 204 | const returnValue = constructor.apply(this, arguments as any) 205 | return (typeof returnValue === 'object' && returnValue) || this 206 | } catch (error) { 207 | // Do our best to only capture errors triggred by class syntax. 208 | // Unfortunately, there's no special error type for this and the 209 | // message is non-standard, so this is the best check we can do. 210 | if ( 211 | error instanceof TypeError && 212 | (/class constructor/i.test(error.message) || /use the 'new' operator/i.test(error.message)) // Custom Elements in Chrome 213 | // TODO: there might be other error messages we need to catch, 214 | // depending on engine and use case. We need to test in all browsers 215 | ) { 216 | // mark this constructor as requiring 'new' for next time 217 | requiresNew = true 218 | } else { 219 | if ( 220 | error instanceof Error && 221 | /Illegal constructor/i.test(error.message) && 222 | Object.create(constructor.prototype) instanceof Node 223 | ) { 224 | console.error( 225 | `The following error can happen if a Custom Element is called 226 | with 'new' before being defined. The constructor was ${constructor.name}: `, 227 | constructor, 228 | ) 229 | } 230 | 231 | throw error 232 | } 233 | } 234 | } 235 | // make a reasonably good replacement for 'new.target' which is a 236 | // syntax error in older engines 237 | var newTarget 238 | var hasNewTarget = false 239 | if (supportsNewTarget) { 240 | eval('newTarget = new.target') 241 | if (newTarget) hasNewTarget = true 242 | } 243 | if (!supportsNewTarget || !hasNewTarget) { 244 | newTarget = this instanceof newlessConstructor ? this.constructor : constructor 245 | } 246 | const returnValue = construct(constructor, arguments, newTarget) 247 | // best effort to make things easy for functions inheriting from classes 248 | if (this instanceof newlessConstructor) { 249 | setPrototype(this, returnValue) 250 | } 251 | return returnValue 252 | })() as unknown as CtorWithLength 253 | 254 | if (name) { 255 | const code = getFunctionBody(newlessConstructor) 256 | 257 | newlessConstructor = Function( 258 | 'constructor, construct, setPrototype, requiresNew, supportsNewTarget', 259 | ` 260 | var newlessConstructor = function ${name}() { ${code} }; 261 | return newlessConstructor 262 | `, 263 | )(constructor, construct, setPrototype, requiresNew, supportsNewTarget) 264 | } 265 | 266 | // copy the `.length` value to the newless constructor 267 | if (constructor.length) { 268 | // length is not writable, only configurable, therefore the value 269 | // has to be set with a descriptor update 270 | setDescriptor(newlessConstructor, 'length', { 271 | value: constructor.length, 272 | }) 273 | } 274 | 275 | newlessConstructor.prototype = Object.create(constructor.prototype) 276 | newlessConstructor.prototype.constructor = newlessConstructor 277 | 278 | // NOTE: *usually* the below will already be true, but we ensure it here. 279 | // Safari 9 requires this for the 'super' keyword to work. Newer versions 280 | // of WebKit and other engines do not. Instead, they use the constructor's 281 | // prototype chain (which is correct by ES2015 spec) (see below). 282 | constructor.prototype.constructor = constructor 283 | 284 | // for ES2015 classes, we need to make sure the constructor's prototype 285 | // is the super class's constructor. Further, optimize performance by 286 | // pointing at the actual constructor implementation instead of the 287 | // newless wrapper (in the case that it is wrapped by newless). 288 | ;(newlessConstructor as any)[TRUE_CONSTRUCTOR] = constructor 289 | 290 | copyProperties(constructor, newlessConstructor) 291 | setPrototype(newlessConstructor, constructor) 292 | 293 | return newlessConstructor as FuncLikeCtor, T> 294 | } 295 | 296 | // Test whether a given syntax is supported 297 | function isSyntaxSupported(example: string, useStrict = true): boolean { 298 | try { 299 | return !!Function('', (useStrict ? "'use strict';" : '') + example) 300 | } catch (error) { 301 | return false 302 | } 303 | } 304 | 305 | type CtorWithLength = Constructor< 306 | object, 307 | any[], 308 | { 309 | length: number 310 | } 311 | > 312 | -------------------------------------------------------------------------------- /dist/native.js: -------------------------------------------------------------------------------- 1 | // borrowed from (and slightly modified) https://github.com/Mr0grog/newless 2 | // The newless license is BSD 3: 3 | // TODO no any types 4 | /* 5 | * Copyright (c) 2013-2016, Rob Brackett 6 | * Copyright (c) 2018, Joseph Orbegoso Pea 7 | * All rights reserved. 8 | * 9 | * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 10 | * 11 | * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 12 | * 13 | * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 14 | * 15 | * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 18 | */ 19 | import { getFunctionBody, setDescriptor } from './utils.js'; 20 | import { Constructor } from './Constructor.js'; 21 | export { newless as native }; 22 | export default newless; 23 | var supportsSpread = isSyntaxSupported('Object(...[{}])'); 24 | var supportsClass = isSyntaxSupported('class Test {}'); 25 | var supportsNewTarget = isSyntaxSupported('new.target'); 26 | // Used to track the original wrapped constructor on a newless instance 27 | var TRUE_CONSTRUCTOR = Symbol ? Symbol('trueConstructor') : '__newlessTrueConstructor__'; 28 | var setPrototype = Object.setPrototypeOf || 29 | function setPrototypeOf(object, newPrototype) { 30 | object.__proto__ = newPrototype; 31 | }; 32 | // Polyfill for Reflect.construct 33 | var construct = (Reflect && Reflect.construct) || 34 | (function () { 35 | if (supportsClass) { 36 | return Function('constructor, args, target', ` 37 | 'use strict'; 38 | 39 | if (arguments.length === 3 && typeof target !== 'function') 40 | throw new TypeError(target + ' is not a constructor'); 41 | 42 | target = target || constructor; 43 | 44 | // extend target so the right prototype is constructed (or nearly the 45 | // right one; ideally we'd do instantiator.prototype = target.prototype, 46 | // but a class's prototype property is not writable) 47 | class instantiator extends target {}; 48 | // but ensure the *logic* is 'constructor' for ES2015-compliant engines 49 | Object.setPrototypeOf(instantiator, constructor); 50 | // ...and for Safari 9 51 | instantiator.prototype.constructor = constructor; 52 | 53 | // The spread operator is *dramatically faster, so use it if we can: 54 | // http://jsperf.com/new-via-spread-vs-dynamic-function/4 55 | ${supportsSpread 56 | ? ` 57 | 58 | var value = new instantiator(...([].slice.call(args))); 59 | 60 | ` 61 | : ` 62 | 63 | // otherwise, create a dynamic function in order to use 'new' 64 | // Note using 'function.bind' would be simpler, but is much slower: 65 | // http://jsperf.com/new-operator-with-dynamic-function-vs-bind 66 | var argList = ''; 67 | for (var i = 0, len = args.length; i < len; i++) { 68 | if (i > 0) argList += ','; 69 | argList += 'args[' + i + ']'; 70 | } 71 | var constructCall = Function('constructor, args', 72 | 'return new constructor( ' + argList + ' );' 73 | ); 74 | var value = constructCall(constructor, args); 75 | 76 | args = Array.prototype.slice.call(args); 77 | args = [null].concat(args); 78 | var value = new constructor.bind.apply(constructor, args); 79 | 80 | `} 81 | 82 | // fix up the prototype so it matches the intended one, not one who's 83 | // prototype is the intended one :P 84 | Object.setPrototypeOf(value, target.prototype); 85 | return value; 86 | `); 87 | //return Function("constructor, args, newTarget", ` 88 | // 'use strict'; 89 | // if (arguments.length === 3 && typeof newTarget === undefined) 90 | // throw new TypeError('undefined is not a constructor'); 91 | // newTarget = newTarget || constructor; 92 | // ${ supportsSpread ? ` 93 | // var value = new constructor(...([].slice.call(args))); 94 | // `:` 95 | // args = Array.prototype.slice.call(args); 96 | // args = [null].concat(args); 97 | // var value = new constructor.bind.apply(constructor, args); 98 | // `} 99 | // Object.setPrototypeOf(value, newTarget.prototype); 100 | // return value; 101 | //`); 102 | } 103 | else { 104 | var instantiator = function () { }; 105 | return function construct(constructor, args, target) { 106 | if (arguments.length === 3 && typeof target !== 'function') 107 | throw new TypeError(target + ' is not a constructor'); 108 | instantiator.prototype = (target || constructor).prototype; 109 | var instance = new instantiator(); 110 | var value = constructor.apply(instance, args); 111 | if (typeof value === 'object' && value) { 112 | // we can do better if __proto__ is available (in some ES5 environments) 113 | value.__proto__ = (target || constructor).prototype; 114 | return value; 115 | } 116 | return instance; 117 | }; 118 | } 119 | })(); 120 | // ES2015 class methods are non-enumerable; we need a helper for copying them. 121 | var SKIP_PROPERTIES = ['arguments', 'caller', 'length', 'name', 'prototype']; 122 | function copyProperties(source, destination) { 123 | if (Object.getOwnPropertyNames && Object.defineProperty) { 124 | var properties = Object.getOwnPropertyNames(source); 125 | if (Object.getOwnPropertySymbols) { 126 | properties = properties.concat(Object.getOwnPropertySymbols(source)); 127 | } 128 | for (var i = properties.length - 1; i >= 0; i--) { 129 | if (SKIP_PROPERTIES.indexOf(properties[i]) === -1) { 130 | Object.defineProperty(destination, properties[i], Object.getOwnPropertyDescriptor(source, properties[i])); 131 | } 132 | } 133 | } 134 | else { 135 | for (var property in source) { 136 | destination[property] = source[property]; 137 | } 138 | } 139 | } 140 | function newless(constructor) { 141 | var name = constructor.name; 142 | // V8 and newer versions of JSCore return the full class declaration from 143 | // `toString()`, which lets us be a little smarter and more performant 144 | // about what to do, since we know we are dealing with a "class". Note, 145 | // however, not all engines do this. This could be false and the constructor 146 | // might still use class syntax. 147 | var usesClassSyntax = constructor.toString().substr(0, 5) === 'class'; 148 | var requiresNew = usesClassSyntax ? true : null; 149 | var newlessConstructor = (() => function () { 150 | // If called with an already valid 'this', preserve that 'this' value 151 | // in the super-type's constructor whenever possible. With function 152 | // constructors (as opposed to class constructors), it's possible to 153 | // alter the instance before calling the super constructor--so it's 154 | // important to preserve that instance if at all possible. 155 | if (!requiresNew && this instanceof newlessConstructor) { 156 | // requiresNew = 'false' indicates we know the 'new' operator isn't 157 | // necessary for this constructor, but 'null' indicates uncertainty, 158 | // so the call needs to handle potential errors the first time in 159 | // order to determine whether 'new' is definitely required. 160 | if (requiresNew === false) { 161 | const returnValue = constructor.apply(this, arguments); 162 | return (typeof returnValue === 'object' && returnValue) || this; 163 | } 164 | try { 165 | requiresNew = false; 166 | const returnValue = constructor.apply(this, arguments); 167 | return (typeof returnValue === 'object' && returnValue) || this; 168 | } 169 | catch (error) { 170 | // Do our best to only capture errors triggred by class syntax. 171 | // Unfortunately, there's no special error type for this and the 172 | // message is non-standard, so this is the best check we can do. 173 | if (error instanceof TypeError && 174 | (/class constructor/i.test(error.message) || /use the 'new' operator/i.test(error.message)) // Custom Elements in Chrome 175 | // TODO: there might be other error messages we need to catch, 176 | // depending on engine and use case. We need to test in all browsers 177 | ) { 178 | // mark this constructor as requiring 'new' for next time 179 | requiresNew = true; 180 | } 181 | else { 182 | if (error instanceof Error && 183 | /Illegal constructor/i.test(error.message) && 184 | Object.create(constructor.prototype) instanceof Node) { 185 | console.error(`The following error can happen if a Custom Element is called 186 | with 'new' before being defined. The constructor was ${constructor.name}: `, constructor); 187 | } 188 | throw error; 189 | } 190 | } 191 | } 192 | // make a reasonably good replacement for 'new.target' which is a 193 | // syntax error in older engines 194 | var newTarget; 195 | var hasNewTarget = false; 196 | if (supportsNewTarget) { 197 | eval('newTarget = new.target'); 198 | if (newTarget) 199 | hasNewTarget = true; 200 | } 201 | if (!supportsNewTarget || !hasNewTarget) { 202 | newTarget = this instanceof newlessConstructor ? this.constructor : constructor; 203 | } 204 | const returnValue = construct(constructor, arguments, newTarget); 205 | // best effort to make things easy for functions inheriting from classes 206 | if (this instanceof newlessConstructor) { 207 | setPrototype(this, returnValue); 208 | } 209 | return returnValue; 210 | })(); 211 | if (name) { 212 | const code = getFunctionBody(newlessConstructor); 213 | newlessConstructor = Function('constructor, construct, setPrototype, requiresNew, supportsNewTarget', ` 214 | var newlessConstructor = function ${name}() { ${code} }; 215 | return newlessConstructor 216 | `)(constructor, construct, setPrototype, requiresNew, supportsNewTarget); 217 | } 218 | // copy the `.length` value to the newless constructor 219 | if (constructor.length) { 220 | // length is not writable, only configurable, therefore the value 221 | // has to be set with a descriptor update 222 | setDescriptor(newlessConstructor, 'length', { 223 | value: constructor.length, 224 | }); 225 | } 226 | newlessConstructor.prototype = Object.create(constructor.prototype); 227 | newlessConstructor.prototype.constructor = newlessConstructor; 228 | // NOTE: *usually* the below will already be true, but we ensure it here. 229 | // Safari 9 requires this for the 'super' keyword to work. Newer versions 230 | // of WebKit and other engines do not. Instead, they use the constructor's 231 | // prototype chain (which is correct by ES2015 spec) (see below). 232 | constructor.prototype.constructor = constructor; 233 | newlessConstructor[TRUE_CONSTRUCTOR] = constructor; 234 | copyProperties(constructor, newlessConstructor); 235 | setPrototype(newlessConstructor, constructor); 236 | return newlessConstructor; 237 | } 238 | // Test whether a given syntax is supported 239 | function isSyntaxSupported(example, useStrict = true) { 240 | try { 241 | return !!Function('', (useStrict ? "'use strict';" : '') + example); 242 | } 243 | catch (error) { 244 | return false; 245 | } 246 | } 247 | //# sourceMappingURL=native.js.map -------------------------------------------------------------------------------- /src/multiple.ts: -------------------------------------------------------------------------------- 1 | import type {Constructor} from './Constructor.js' 2 | 3 | // --- TODO handle static inheritance. Nothing has been implemented with regards to 4 | // static inheritance yet. 5 | 6 | // --- TODO allow the subclass (f.e. the `Foo` in `class Foo extends multiple(One, 7 | // Two, Three) {}`) to call each super constructor (One, Two, and Three) 8 | // individually with specific arguments. 9 | 10 | // --- TODO Prevent duplicate classes in the "prototype tree". F.e. if someone calls 11 | // `multiple(One, Two, Three)`, and `Three` already includes `Two`, we can 12 | // discard the `Two` argument and perform the combination as if `multiple(One, 13 | // Three)` had been called. 14 | 15 | // --- TODO cache the results, so more than one call to `multiple(One, Two, Three)` 16 | // returns the same class reference as the first call. 17 | 18 | // --- TODO, allow the user to handle the diamond problem in some way other than 19 | // ("property or method from the first class in the list wins"). Perhaps require 20 | // the user to specify which method to call. For now, it simply calls the first 21 | // method in the order in which the classes were passed into multiple(). Look 22 | // here for ideas based on how different languages handle it: 23 | // https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem 24 | 25 | enum ImplementationMethod { 26 | PROXIES_ON_INSTANCE_AND_PROTOTYPE = 'PROXIES_ON_INSTANCE_AND_PROTOTYPE', 27 | PROXIES_ON_PROTOTYPE = 'PROXIES_ON_PROTOTYPE', 28 | 29 | // TODO, This will be similar to PROXIES_ON_INSTANCE_AND_PROTOTYPE, but 30 | // instead of placing a proxy on the instance, place a Proxy as a direct 31 | // prototype of the instance. I think this should work with Custom Elements, 32 | // and unlike PROXIES_ON_PROTOTYPE, super calls won't access own properties 33 | // on the instance, but actually on the prototypes (test5 super access tests 34 | // fail with PROXIES_ON_PROTOTYPE method). 35 | PROXY_AFTER_INSTANCE_AND_PROTOTYPE = 'PROXY_AFTER_INSTANCE_AND_PROTOTYPE', 36 | } 37 | 38 | type MultipleOptions = { 39 | method: ImplementationMethod 40 | } 41 | 42 | export function makeMultipleHelper(options?: MultipleOptions) { 43 | /** 44 | * Mixes the given classes into a single class. This is useful for multiple 45 | * inheritance. 46 | * 47 | * @example 48 | * class Foo {} 49 | * class Bar {} 50 | * class Baz {} 51 | * class MyClass extends multiple(Foo, Bar, Baz) {} 52 | */ 53 | // ------------ method 1, define the `multiple()` signature with overrides. The 54 | // upside is it is easy to understand, but the downside is that name collisions 55 | // in properties cause the collided property type to be `never`. This would make 56 | // it more difficult to provide solution for the diamond problem. 57 | // ---------------- 58 | // function multiple(): typeof Object 59 | // function multiple(classes: T): T 60 | // function multiple(...classes: T): Constructor> 61 | // function multiple(...classes: any): any { 62 | // 63 | // ------------ method 2, define the signature of `multiple()` with a single 64 | // signature. The upside is this picks the type of the first property 65 | // encountered when property names collide amongst all the classes passed into 66 | // `multiple()`, but the downside is the inner implementation may require 67 | // casting, and this approach can also cause an infinite type recursion 68 | // depending on the types used inside the implementation. 69 | // ---------------- 70 | return function multiple(...classes: T): CombinedClasses { 71 | const mode = (options && options.method) || ImplementationMethod.PROXIES_ON_INSTANCE_AND_PROTOTYPE 72 | 73 | switch (mode) { 74 | case ImplementationMethod.PROXIES_ON_INSTANCE_AND_PROTOTYPE: { 75 | return (withProxiesOnThisAndPrototype as any)(...classes) 76 | } 77 | case ImplementationMethod.PROXIES_ON_PROTOTYPE: { 78 | return (withProxiesOnPrototype as any)(...classes) 79 | } 80 | case ImplementationMethod.PROXY_AFTER_INSTANCE_AND_PROTOTYPE: { 81 | throw new Error(' not implemented yet') 82 | } 83 | } 84 | } 85 | } 86 | 87 | /** 88 | * Mixes the given classes into a single class. This is useful for multiple 89 | * inheritance. 90 | * 91 | * @example 92 | * class Foo {} 93 | * class Bar {} 94 | * class Baz {} 95 | * class MyClass extends multiple(Foo, Bar, Baz) {} 96 | */ 97 | export const multiple = makeMultipleHelper({method: ImplementationMethod.PROXIES_ON_INSTANCE_AND_PROTOTYPE}) 98 | // export const multiple = makeMultipleHelper({method: ImplementationMethod.PROXIES_ON_PROTOTYPE}) 99 | 100 | function withProxiesOnThisAndPrototype(...classes: T): CombinedClasses { 101 | // avoid performance costs in special cases 102 | if (classes.length === 0) return Object as any 103 | if (classes.length === 1) return classes[0] as any 104 | 105 | const FirstClass = classes.shift()! 106 | 107 | // inherit the first class normally. This allows for required native 108 | // inheritance in certain special cases (like inheriting from HTMLElement 109 | // when making Custom Elements). 110 | class MultiClass extends FirstClass { 111 | constructor(...args: any[]) { 112 | super(...args) 113 | 114 | const instances: Object[] = [] 115 | 116 | // make instances of the other classes to get/set properties on. 117 | let Ctor: Constructor 118 | for (let i = 0, l = classes.length; i < l; i += 1) { 119 | Ctor = classes[i]! 120 | const instance = Reflect.construct(Ctor, args) 121 | instances.push(instance) 122 | } 123 | 124 | return new Proxy(this, { 125 | // No `set()` trap is needed in this Proxy handler, at least for 126 | // the tests so far. Methods automatically have the correct 127 | // receiver when the are gotten with the `get()` trap, so if any 128 | // methods set a property, the set happens on the expected 129 | // instance, just like regular [[Set]]. 130 | 131 | get(target, key: string | symbol, self: MultiClass): any { 132 | if (Reflect.ownKeys(target).includes(key)) return Reflect.get(target, key, self) 133 | 134 | let instance: Object 135 | 136 | for (let i = 0, l = instances.length; i < l; i += 1) { 137 | instance = instances[i]! 138 | if (Reflect.ownKeys(instance).includes(key)) return Reflect.get(instance, key, self) 139 | } 140 | 141 | const proto = Object.getPrototypeOf(self) 142 | if (Reflect.has(proto, key)) return Reflect.get(proto, key, self) 143 | 144 | return undefined 145 | }, 146 | 147 | ownKeys(target) { 148 | let keys = Reflect.ownKeys(target) 149 | 150 | let instance: Object 151 | let instanceKeys: (string | symbol)[] 152 | 153 | for (let i = 0, l = instances.length; i < l; i += 1) { 154 | instance = instances[i]! 155 | instanceKeys = Reflect.ownKeys(instance) 156 | for (let j = 0, l = instanceKeys.length; j < l; j += 1) keys.push(instanceKeys[j]!) 157 | } 158 | 159 | return keys 160 | }, 161 | 162 | // This makes the `in` operator work, for example. 163 | has(target, key: string | symbol): boolean { 164 | if (Reflect.ownKeys(target).includes(key)) return true 165 | 166 | let instance: Object 167 | for (let i = 0, l = instances.length; i < l; i += 1) { 168 | instance = instances[i]! 169 | if (Reflect.ownKeys(instance).includes(key)) return true 170 | } 171 | 172 | // all instances share the same prototype, so just check it once 173 | const proto = Object.getPrototypeOf(self) 174 | if (Reflect.has(proto, key)) return true 175 | 176 | return false 177 | }, 178 | }) 179 | } 180 | } 181 | 182 | const newMultiClassPrototype = new Proxy(Object.create(FirstClass.prototype), { 183 | get(target, key: string | symbol, self: MultiClass): any { 184 | if (Reflect.has(target, key)) return Reflect.get(target, key, self) 185 | 186 | let Class: Constructor 187 | for (let i = 0, l = classes.length; i < l; i += 1) { 188 | Class = classes[i]! 189 | if (Reflect.has(Class.prototype, key)) return Reflect.get(Class.prototype, key, self) 190 | } 191 | }, 192 | 193 | has(target, key): boolean { 194 | if (Reflect.has(target, key)) return true 195 | 196 | let Class: Constructor 197 | for (let i = 0, l = classes.length; i < l; i += 1) { 198 | Class = classes[i]! 199 | if (Reflect.has(Class.prototype, key)) return true 200 | } 201 | 202 | return false 203 | }, 204 | }) 205 | 206 | // This is so that `super` calls will work. We can't replace 207 | // MultiClass.prototype with a Proxy because MultiClass.prototype is 208 | // non-configurable, so it is impossible to wrap it with a Proxy. Instead, 209 | // we stick our own custom Proxy-wrapped prototype object between 210 | // MultiClass.prototype and FirstClass.prototype. 211 | Object.setPrototypeOf(MultiClass.prototype, newMultiClassPrototype) 212 | 213 | return MultiClass as unknown as CombinedClasses 214 | } 215 | 216 | let currentSelf: Object[] = [] 217 | 218 | const __instances__ = new WeakMap() 219 | const getInstances = (inst: object): Object[] => { 220 | let result = __instances__.get(inst) 221 | if (!result) __instances__.set(inst, (result = [])) 222 | return result 223 | } 224 | 225 | // function hasKey(instance: object, key: string | number | symbol, traverse: boolean = true): boolean { 226 | // if (Reflect.ownKeys(instance).includes(key)) return true 227 | 228 | // if (!traverse) return false 229 | 230 | // const instances = __instances__.get(instance) 231 | // if (!instances) return false 232 | 233 | // for (const instance of instances) if (hasKey(instance, key, true)) return true 234 | 235 | // return false 236 | // } 237 | 238 | type GetResult = {has: boolean; value: any} 239 | 240 | const getResult: GetResult = {has: false, value: undefined} 241 | 242 | function getFromInstance(instance: object, key: string | symbol, result: GetResult): void { 243 | result.has = false 244 | result.value = undefined 245 | 246 | if (Reflect.ownKeys(instance).includes(key)) { 247 | result.has = true 248 | result.value = Reflect.get(instance, key) 249 | return 250 | } 251 | 252 | const instances = __instances__.get(instance) 253 | if (!instances) return 254 | 255 | for (const instance of instances) { 256 | // if (hasKey(instance, key, true)) { 257 | // getFromInstance(instance, key, result) 258 | // return 259 | // } 260 | 261 | getFromInstance(instance, key, result) 262 | if (result.has) return 263 | } 264 | } 265 | 266 | let shouldGetFromPrototype = false 267 | let topLevelMultiClassPrototype: object | null = null 268 | 269 | function withProxiesOnPrototype(...classes: T): CombinedClasses { 270 | // avoid performance costs in special cases 271 | if (classes.length === 0) return Object as any 272 | if (classes.length === 1) return classes[0] as any 273 | 274 | const FirstClass = classes.shift()! 275 | 276 | // inherit the first class normally. This allows for required native 277 | // inheritance in certain special cases (like inheriting from HTMLElement 278 | // when making Custom Elements). 279 | class MultiClass extends FirstClass { 280 | constructor(...args: any[]) { 281 | super(...args) 282 | 283 | // This assumes no super constructor returns a different this from 284 | // their constructor. Otherwise the getInstances call won't work as 285 | // expected. 286 | const instances = getInstances(this) 287 | 288 | // make instances of the other classes to get/set properties on. 289 | for (const Ctor of classes) { 290 | const instance = Reflect.construct(Ctor, args) 291 | instances.push(instance) 292 | } 293 | } 294 | } 295 | 296 | const newMultiClassPrototype = new Proxy(Object.create(FirstClass.prototype), { 297 | get(target, key: string | symbol, self: MultiClass): any { 298 | if (!topLevelMultiClassPrototype) topLevelMultiClassPrototype = target 299 | 300 | if (!shouldGetFromPrototype) { 301 | getFromInstance(self, key, getResult) 302 | 303 | if (getResult.has) { 304 | topLevelMultiClassPrototype = null 305 | return getResult.value 306 | } 307 | 308 | // only the top level MultiClass subclass prototype will check 309 | // instances for a property. The superclass MultiClass 310 | // prototypes will do a regular prototype get. 311 | shouldGetFromPrototype = true 312 | } 313 | 314 | // TODO, I think instead of passing `self` we should be passing the 315 | // instances created from the classes? We need to write more tests, 316 | // especially ones that create new properties later and not at 317 | // construction time. 318 | if (shouldGetFromPrototype) { 319 | let result: any = undefined 320 | 321 | if (Reflect.has(target, key)) result = Reflect.get(target, key, self) 322 | 323 | let Class: Constructor 324 | for (let i = 0, l = classes.length; i < l; i += 1) { 325 | Class = classes[i]! 326 | if (Reflect.has(Class.prototype, key)) result = Reflect.get(Class.prototype, key, self) 327 | } 328 | 329 | if (topLevelMultiClassPrototype === target) { 330 | topLevelMultiClassPrototype = null 331 | shouldGetFromPrototype = false 332 | } 333 | 334 | return result 335 | } 336 | 337 | // currentSelf.push(self) 338 | 339 | // if (Reflect.ownKeys(self).includes(key)) { 340 | // currentSelf.pop() 341 | // return Reflect.get(target, key, self) 342 | // } 343 | 344 | // currentSelf.pop() 345 | 346 | // for (const instance of getInstances(self)) { 347 | // currentSelf.push(instance) 348 | 349 | // if (Reflect.ownKeys(instance).includes(key)) { 350 | // currentSelf.pop() 351 | // return Reflect.get(instance, key, instance) 352 | // } 353 | 354 | // currentSelf.pop() 355 | // } 356 | 357 | // return undefined 358 | }, 359 | 360 | set(target, key: string | symbol, value: any, self): boolean { 361 | currentSelf.push(self) 362 | 363 | // If the key is in the current prototype chain, continue like normal... 364 | if (Reflect.has(target, key)) { 365 | currentSelf.pop() 366 | return Reflect.set(target, key, value, self) 367 | } 368 | 369 | currentSelf.pop() 370 | 371 | // ...Otherwise if the key isn't, set it on one of the instances of the classes. 372 | for (const instance of getInstances(self)) { 373 | currentSelf.push(instance) 374 | 375 | if (Reflect.has(instance, key)) { 376 | currentSelf.pop() 377 | return Reflect.set(instance, key, value, instance) 378 | // return Reflect.set(instance, key, value, self) 379 | } 380 | 381 | currentSelf.pop() 382 | } 383 | 384 | // If the key is not found, set it like normal. 385 | return Reflect.set(target, key, value, self) 386 | }, 387 | 388 | has(target, key): boolean { 389 | // if (currentSelf.length) { 390 | // let current = currentSelf[currentSelf.length - 1] 391 | 392 | // while (current) { 393 | // if (Reflect.ownKeys(current).includes(key)) return true 394 | // current = Reflect.getPrototypeOf(current) as MultiClass 395 | // } 396 | 397 | // for (const instance of getInstances(current as MultiClass)) 398 | // if (Reflect.has(instance, key)) return true 399 | // } else { 400 | if (Reflect.has(target, key)) return true 401 | 402 | let Class: Constructor 403 | for (let i = 0, l = classes.length; i < l; i += 1) { 404 | Class = classes[i]! 405 | if (Reflect.has(Class.prototype, key)) return true 406 | } 407 | // } 408 | 409 | return false 410 | }, 411 | }) 412 | 413 | // This is so that `super` calls will work. We can't replace 414 | // MultiClass.prototype with a Proxy because MultiClass.prototype is 415 | // non-configurable, so it is impossible to wrap it with a Proxy. Instead, 416 | // we stick our own custom Proxy-wrapped prototype object between 417 | // MultiClass.prototype and FirstClass.prototype. 418 | Object.setPrototypeOf(MultiClass.prototype, newMultiClassPrototype) 419 | 420 | return MultiClass as unknown as CombinedClasses 421 | } 422 | 423 | // type ConstructorUnionToInstanceTypeUnion = (U extends Constructor 424 | // ? (k: InstanceType) => void 425 | // : never) extends (k: infer I) => void 426 | // ? I 427 | // : never 428 | 429 | type Shift = ((...args: T) => any) extends (_: any, ...args: infer R) => any ? R : never 430 | type MixedArray[]> = _MixedArray 431 | type _MixedArray[], U> = { 432 | 0: new () => U 433 | 1: _MixedArray< 434 | Shift, 435 | { 436 | [K in keyof InstanceType | keyof U]: K extends keyof U ? U[K] : InstanceType[K] 437 | } 438 | > 439 | }[T['length'] extends 0 ? 0 : 1] 440 | 441 | type CombinedClasses = T extends [] | [undefined] 442 | ? typeof Object 443 | : T extends Constructor[] 444 | ? MixedArray 445 | : typeof Object 446 | -------------------------------------------------------------------------------- /dist/multiple.js: -------------------------------------------------------------------------------- 1 | // --- TODO handle static inheritance. Nothing has been implemented with regards to 2 | // static inheritance yet. 3 | // --- TODO allow the subclass (f.e. the `Foo` in `class Foo extends multiple(One, 4 | // Two, Three) {}`) to call each super constructor (One, Two, and Three) 5 | // individually with specific arguments. 6 | // --- TODO Prevent duplicate classes in the "prototype tree". F.e. if someone calls 7 | // `multiple(One, Two, Three)`, and `Three` already includes `Two`, we can 8 | // discard the `Two` argument and perform the combination as if `multiple(One, 9 | // Three)` had been called. 10 | // --- TODO cache the results, so more than one call to `multiple(One, Two, Three)` 11 | // returns the same class reference as the first call. 12 | // --- TODO, allow the user to handle the diamond problem in some way other than 13 | // ("property or method from the first class in the list wins"). Perhaps require 14 | // the user to specify which method to call. For now, it simply calls the first 15 | // method in the order in which the classes were passed into multiple(). Look 16 | // here for ideas based on how different languages handle it: 17 | // https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem 18 | var ImplementationMethod; 19 | (function (ImplementationMethod) { 20 | ImplementationMethod["PROXIES_ON_INSTANCE_AND_PROTOTYPE"] = "PROXIES_ON_INSTANCE_AND_PROTOTYPE"; 21 | ImplementationMethod["PROXIES_ON_PROTOTYPE"] = "PROXIES_ON_PROTOTYPE"; 22 | // TODO, This will be similar to PROXIES_ON_INSTANCE_AND_PROTOTYPE, but 23 | // instead of placing a proxy on the instance, place a Proxy as a direct 24 | // prototype of the instance. I think this should work with Custom Elements, 25 | // and unlike PROXIES_ON_PROTOTYPE, super calls won't access own properties 26 | // on the instance, but actually on the prototypes (test5 super access tests 27 | // fail with PROXIES_ON_PROTOTYPE method). 28 | ImplementationMethod["PROXY_AFTER_INSTANCE_AND_PROTOTYPE"] = "PROXY_AFTER_INSTANCE_AND_PROTOTYPE"; 29 | })(ImplementationMethod || (ImplementationMethod = {})); 30 | export function makeMultipleHelper(options) { 31 | /** 32 | * Mixes the given classes into a single class. This is useful for multiple 33 | * inheritance. 34 | * 35 | * @example 36 | * class Foo {} 37 | * class Bar {} 38 | * class Baz {} 39 | * class MyClass extends multiple(Foo, Bar, Baz) {} 40 | */ 41 | // ------------ method 1, define the `multiple()` signature with overrides. The 42 | // upside is it is easy to understand, but the downside is that name collisions 43 | // in properties cause the collided property type to be `never`. This would make 44 | // it more difficult to provide solution for the diamond problem. 45 | // ---------------- 46 | // function multiple(): typeof Object 47 | // function multiple(classes: T): T 48 | // function multiple(...classes: T): Constructor> 49 | // function multiple(...classes: any): any { 50 | // 51 | // ------------ method 2, define the signature of `multiple()` with a single 52 | // signature. The upside is this picks the type of the first property 53 | // encountered when property names collide amongst all the classes passed into 54 | // `multiple()`, but the downside is the inner implementation may require 55 | // casting, and this approach can also cause an infinite type recursion 56 | // depending on the types used inside the implementation. 57 | // ---------------- 58 | return function multiple(...classes) { 59 | const mode = (options && options.method) || ImplementationMethod.PROXIES_ON_INSTANCE_AND_PROTOTYPE; 60 | switch (mode) { 61 | case ImplementationMethod.PROXIES_ON_INSTANCE_AND_PROTOTYPE: { 62 | return withProxiesOnThisAndPrototype(...classes); 63 | } 64 | case ImplementationMethod.PROXIES_ON_PROTOTYPE: { 65 | return withProxiesOnPrototype(...classes); 66 | } 67 | case ImplementationMethod.PROXY_AFTER_INSTANCE_AND_PROTOTYPE: { 68 | throw new Error(' not implemented yet'); 69 | } 70 | } 71 | }; 72 | } 73 | /** 74 | * Mixes the given classes into a single class. This is useful for multiple 75 | * inheritance. 76 | * 77 | * @example 78 | * class Foo {} 79 | * class Bar {} 80 | * class Baz {} 81 | * class MyClass extends multiple(Foo, Bar, Baz) {} 82 | */ 83 | export const multiple = makeMultipleHelper({ method: ImplementationMethod.PROXIES_ON_INSTANCE_AND_PROTOTYPE }); 84 | // export const multiple = makeMultipleHelper({method: ImplementationMethod.PROXIES_ON_PROTOTYPE}) 85 | function withProxiesOnThisAndPrototype(...classes) { 86 | // avoid performance costs in special cases 87 | if (classes.length === 0) 88 | return Object; 89 | if (classes.length === 1) 90 | return classes[0]; 91 | const FirstClass = classes.shift(); 92 | // inherit the first class normally. This allows for required native 93 | // inheritance in certain special cases (like inheriting from HTMLElement 94 | // when making Custom Elements). 95 | class MultiClass extends FirstClass { 96 | constructor(...args) { 97 | super(...args); 98 | const instances = []; 99 | // make instances of the other classes to get/set properties on. 100 | let Ctor; 101 | for (let i = 0, l = classes.length; i < l; i += 1) { 102 | Ctor = classes[i]; 103 | const instance = Reflect.construct(Ctor, args); 104 | instances.push(instance); 105 | } 106 | return new Proxy(this, { 107 | // No `set()` trap is needed in this Proxy handler, at least for 108 | // the tests so far. Methods automatically have the correct 109 | // receiver when the are gotten with the `get()` trap, so if any 110 | // methods set a property, the set happens on the expected 111 | // instance, just like regular [[Set]]. 112 | get(target, key, self) { 113 | if (Reflect.ownKeys(target).includes(key)) 114 | return Reflect.get(target, key, self); 115 | let instance; 116 | for (let i = 0, l = instances.length; i < l; i += 1) { 117 | instance = instances[i]; 118 | if (Reflect.ownKeys(instance).includes(key)) 119 | return Reflect.get(instance, key, self); 120 | } 121 | const proto = Object.getPrototypeOf(self); 122 | if (Reflect.has(proto, key)) 123 | return Reflect.get(proto, key, self); 124 | return undefined; 125 | }, 126 | ownKeys(target) { 127 | let keys = Reflect.ownKeys(target); 128 | let instance; 129 | let instanceKeys; 130 | for (let i = 0, l = instances.length; i < l; i += 1) { 131 | instance = instances[i]; 132 | instanceKeys = Reflect.ownKeys(instance); 133 | for (let j = 0, l = instanceKeys.length; j < l; j += 1) 134 | keys.push(instanceKeys[j]); 135 | } 136 | return keys; 137 | }, 138 | // This makes the `in` operator work, for example. 139 | has(target, key) { 140 | if (Reflect.ownKeys(target).includes(key)) 141 | return true; 142 | let instance; 143 | for (let i = 0, l = instances.length; i < l; i += 1) { 144 | instance = instances[i]; 145 | if (Reflect.ownKeys(instance).includes(key)) 146 | return true; 147 | } 148 | // all instances share the same prototype, so just check it once 149 | const proto = Object.getPrototypeOf(self); 150 | if (Reflect.has(proto, key)) 151 | return true; 152 | return false; 153 | }, 154 | }); 155 | } 156 | } 157 | const newMultiClassPrototype = new Proxy(Object.create(FirstClass.prototype), { 158 | get(target, key, self) { 159 | if (Reflect.has(target, key)) 160 | return Reflect.get(target, key, self); 161 | let Class; 162 | for (let i = 0, l = classes.length; i < l; i += 1) { 163 | Class = classes[i]; 164 | if (Reflect.has(Class.prototype, key)) 165 | return Reflect.get(Class.prototype, key, self); 166 | } 167 | }, 168 | has(target, key) { 169 | if (Reflect.has(target, key)) 170 | return true; 171 | let Class; 172 | for (let i = 0, l = classes.length; i < l; i += 1) { 173 | Class = classes[i]; 174 | if (Reflect.has(Class.prototype, key)) 175 | return true; 176 | } 177 | return false; 178 | }, 179 | }); 180 | // This is so that `super` calls will work. We can't replace 181 | // MultiClass.prototype with a Proxy because MultiClass.prototype is 182 | // non-configurable, so it is impossible to wrap it with a Proxy. Instead, 183 | // we stick our own custom Proxy-wrapped prototype object between 184 | // MultiClass.prototype and FirstClass.prototype. 185 | Object.setPrototypeOf(MultiClass.prototype, newMultiClassPrototype); 186 | return MultiClass; 187 | } 188 | let currentSelf = []; 189 | const __instances__ = new WeakMap(); 190 | const getInstances = (inst) => { 191 | let result = __instances__.get(inst); 192 | if (!result) 193 | __instances__.set(inst, (result = [])); 194 | return result; 195 | }; 196 | const getResult = { has: false, value: undefined }; 197 | function getFromInstance(instance, key, result) { 198 | result.has = false; 199 | result.value = undefined; 200 | if (Reflect.ownKeys(instance).includes(key)) { 201 | result.has = true; 202 | result.value = Reflect.get(instance, key); 203 | return; 204 | } 205 | const instances = __instances__.get(instance); 206 | if (!instances) 207 | return; 208 | for (const instance of instances) { 209 | // if (hasKey(instance, key, true)) { 210 | // getFromInstance(instance, key, result) 211 | // return 212 | // } 213 | getFromInstance(instance, key, result); 214 | if (result.has) 215 | return; 216 | } 217 | } 218 | let shouldGetFromPrototype = false; 219 | let topLevelMultiClassPrototype = null; 220 | function withProxiesOnPrototype(...classes) { 221 | // avoid performance costs in special cases 222 | if (classes.length === 0) 223 | return Object; 224 | if (classes.length === 1) 225 | return classes[0]; 226 | const FirstClass = classes.shift(); 227 | // inherit the first class normally. This allows for required native 228 | // inheritance in certain special cases (like inheriting from HTMLElement 229 | // when making Custom Elements). 230 | class MultiClass extends FirstClass { 231 | constructor(...args) { 232 | super(...args); 233 | // This assumes no super constructor returns a different this from 234 | // their constructor. Otherwise the getInstances call won't work as 235 | // expected. 236 | const instances = getInstances(this); 237 | // make instances of the other classes to get/set properties on. 238 | for (const Ctor of classes) { 239 | const instance = Reflect.construct(Ctor, args); 240 | instances.push(instance); 241 | } 242 | } 243 | } 244 | const newMultiClassPrototype = new Proxy(Object.create(FirstClass.prototype), { 245 | get(target, key, self) { 246 | if (!topLevelMultiClassPrototype) 247 | topLevelMultiClassPrototype = target; 248 | if (!shouldGetFromPrototype) { 249 | getFromInstance(self, key, getResult); 250 | if (getResult.has) { 251 | topLevelMultiClassPrototype = null; 252 | return getResult.value; 253 | } 254 | // only the top level MultiClass subclass prototype will check 255 | // instances for a property. The superclass MultiClass 256 | // prototypes will do a regular prototype get. 257 | shouldGetFromPrototype = true; 258 | } 259 | // TODO, I think instead of passing `self` we should be passing the 260 | // instances created from the classes? We need to write more tests, 261 | // especially ones that create new properties later and not at 262 | // construction time. 263 | if (shouldGetFromPrototype) { 264 | let result = undefined; 265 | if (Reflect.has(target, key)) 266 | result = Reflect.get(target, key, self); 267 | let Class; 268 | for (let i = 0, l = classes.length; i < l; i += 1) { 269 | Class = classes[i]; 270 | if (Reflect.has(Class.prototype, key)) 271 | result = Reflect.get(Class.prototype, key, self); 272 | } 273 | if (topLevelMultiClassPrototype === target) { 274 | topLevelMultiClassPrototype = null; 275 | shouldGetFromPrototype = false; 276 | } 277 | return result; 278 | } 279 | // currentSelf.push(self) 280 | // if (Reflect.ownKeys(self).includes(key)) { 281 | // currentSelf.pop() 282 | // return Reflect.get(target, key, self) 283 | // } 284 | // currentSelf.pop() 285 | // for (const instance of getInstances(self)) { 286 | // currentSelf.push(instance) 287 | // if (Reflect.ownKeys(instance).includes(key)) { 288 | // currentSelf.pop() 289 | // return Reflect.get(instance, key, instance) 290 | // } 291 | // currentSelf.pop() 292 | // } 293 | // return undefined 294 | }, 295 | set(target, key, value, self) { 296 | currentSelf.push(self); 297 | // If the key is in the current prototype chain, continue like normal... 298 | if (Reflect.has(target, key)) { 299 | currentSelf.pop(); 300 | return Reflect.set(target, key, value, self); 301 | } 302 | currentSelf.pop(); 303 | // ...Otherwise if the key isn't, set it on one of the instances of the classes. 304 | for (const instance of getInstances(self)) { 305 | currentSelf.push(instance); 306 | if (Reflect.has(instance, key)) { 307 | currentSelf.pop(); 308 | return Reflect.set(instance, key, value, instance); 309 | // return Reflect.set(instance, key, value, self) 310 | } 311 | currentSelf.pop(); 312 | } 313 | // If the key is not found, set it like normal. 314 | return Reflect.set(target, key, value, self); 315 | }, 316 | has(target, key) { 317 | // if (currentSelf.length) { 318 | // let current = currentSelf[currentSelf.length - 1] 319 | // while (current) { 320 | // if (Reflect.ownKeys(current).includes(key)) return true 321 | // current = Reflect.getPrototypeOf(current) as MultiClass 322 | // } 323 | // for (const instance of getInstances(current as MultiClass)) 324 | // if (Reflect.has(instance, key)) return true 325 | // } else { 326 | if (Reflect.has(target, key)) 327 | return true; 328 | let Class; 329 | for (let i = 0, l = classes.length; i < l; i += 1) { 330 | Class = classes[i]; 331 | if (Reflect.has(Class.prototype, key)) 332 | return true; 333 | } 334 | // } 335 | return false; 336 | }, 337 | }); 338 | // This is so that `super` calls will work. We can't replace 339 | // MultiClass.prototype with a Proxy because MultiClass.prototype is 340 | // non-configurable, so it is impossible to wrap it with a Proxy. Instead, 341 | // we stick our own custom Proxy-wrapped prototype object between 342 | // MultiClass.prototype and FirstClass.prototype. 343 | Object.setPrototypeOf(MultiClass.prototype, newMultiClassPrototype); 344 | return MultiClass; 345 | } 346 | //# sourceMappingURL=multiple.js.map --------------------------------------------------------------------------------