-
Notifications
You must be signed in to change notification settings - Fork 3.6k
/
mix.ts
110 lines (102 loc) · 2.95 KB
/
mix.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @module utils/mix
*/
/**
* Copies enumerable properties and symbols from the objects given as 2nd+ parameters to the
* prototype of first object (a constructor).
*
* ```
* class Editor {
* ...
* }
*
* const SomeMixin = {
* a() {
* return 'a';
* }
* };
*
* mix( Editor, SomeMixin, ... );
*
* new Editor().a(); // -> 'a'
* ```
*
* Note: Properties which already exist in the base class will not be overriden.
*
* @deprecated Use mixin pattern, see: https://www.typescriptlang.org/docs/handbook/mixins.html.
* @param baseClass Class which prototype will be extended.
* @param mixins Objects from which to get properties.
*/
export default function mix( baseClass: Function, ...mixins: Array<object> ): void {
mixins.forEach( mixin => {
const propertyNames: Array<string | symbol> = Object.getOwnPropertyNames( mixin );
const propertySymbols = Object.getOwnPropertySymbols( mixin );
propertyNames.concat( propertySymbols ).forEach( key => {
if ( key in baseClass.prototype ) {
return;
}
if ( typeof mixin == 'function' && ( key == 'length' || key == 'name' || key == 'prototype' ) ) {
return;
}
const sourceDescriptor = Object.getOwnPropertyDescriptor( mixin, key )!;
sourceDescriptor.enumerable = false;
Object.defineProperty( baseClass.prototype, key, sourceDescriptor );
} );
} );
}
/**
* Helper type that represents constructor creating given objects. Can be used as a type constraint.
*
* ```ts
* // The function accepts any class constructor.
* function MyFunction<Ctor extends Constructor>( ctor: Ctor ) {
* // ...
* }
*
* // The function accepts any class constructor of type derived from `MyBase`.
* function MyFunction<Ctor extends Constructor<MyBase>>( ctor: Ctor ) {
* // ...
* }
* ```
*/
export type Constructor<Instance = object> = abstract new ( ...args: Array<any> ) => Instance;
/**
* Helper type that creates constructor types from a base class and a mixin interface.
*
* ```ts
* interface MyMixinInterface {
* mixinMethod(): void;
* }
*
* function MyMixin<Base extends Constructor>( base: Base ): Mixed<Base, MyMixinInterface> {
* // ...
* }
*
* class BaseClass {
* baseMethod(): void {
* // ...
* }
* }
*
* const MixedClass = MyMixin( BaseClass );
*
* // Contains both `mixinMethod()` and `baseMethod()`.
* const myObject = new MixedClass();
* myObject.mixinMethod();
* myObject.baseMethod();
* ```
*
* @typeParam Base A type of constructor of a class to apply mixin to.
* @typeParam Mixin An interface representing mixin.
*/
export type Mixed<Base extends Constructor, Mixin extends object> = {
new ( ...args: ConstructorParameters<Base> ): Mixin & InstanceType<Base>;
prototype: Mixin & InstanceType<Base>;
} & {
// Include all static fields from Base.
[ K in keyof Base ]: Base[ K ];
};