typeomatica 0.2.3 → 0.2.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.husky/pre-commit +4 -0
- package/jest.config.js +1 -1
- package/lib/errors.d.ts +7 -0
- package/lib/errors.js +10 -0
- package/lib/fields.d.ts +11 -0
- package/lib/fields.js +24 -0
- package/lib/index.d.ts +5 -5
- package/lib/index.js +35 -113
- package/lib/types/functions.d.ts +1 -0
- package/lib/types/functions.js +8 -0
- package/lib/types/index.d.ts +6 -0
- package/lib/types/index.js +22 -0
- package/lib/types/nullish.d.ts +4 -0
- package/lib/types/nullish.js +16 -0
- package/lib/types/objects.d.ts +4 -0
- package/lib/types/objects.js +20 -0
- package/lib/types/primitives.d.ts +4 -0
- package/lib/types/primitives.js +48 -0
- package/lib/types/special.d.ts +4 -0
- package/lib/types/special.js +20 -0
- package/package.json +14 -12
- package/src/errors.ts +9 -0
- package/src/fields.ts +73 -0
- package/src/index.ts +64 -146
- package/src/types/functions.ts +7 -0
- package/src/types/index.ts +17 -0
- package/src/types/nullish.ts +15 -0
- package/src/types/objects.ts +19 -0
- package/src/types/primitives.ts +68 -0
- package/src/types/special.ts +19 -0
- package/test/index.ts +154 -7
- package/{tsconfig.spec.json → test/tsconfig.json} +5 -5
package/jest.config.js
CHANGED
package/lib/errors.d.ts
ADDED
package/lib/errors.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ErrorsNames = void 0;
|
|
4
|
+
exports.ErrorsNames = {
|
|
5
|
+
TYPE_MISMATCH: 'Type Mismatch',
|
|
6
|
+
ACCESS_DENIED: 'Value Access Denied',
|
|
7
|
+
MISSING_PROP: 'Attempt to Access to Undefined Prop',
|
|
8
|
+
RIP_FUNCTIONS: 'Functions are Restricted',
|
|
9
|
+
FORBIDDEN_RE: 'Re-Assirnment is Forbidden'
|
|
10
|
+
};
|
package/lib/fields.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare const SymbolInitialValue: unique symbol;
|
|
2
|
+
interface FieldDefinition {
|
|
3
|
+
[SymbolInitialValue]: unknown;
|
|
4
|
+
}
|
|
5
|
+
export declare class FieldConstructor implements FieldDefinition {
|
|
6
|
+
[SymbolInitialValue]: unknown;
|
|
7
|
+
get get(): (this: FieldDefinition) => unknown;
|
|
8
|
+
get set(): () => never;
|
|
9
|
+
constructor(value: unknown);
|
|
10
|
+
}
|
|
11
|
+
export {};
|
package/lib/fields.js
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FieldConstructor = exports.SymbolInitialValue = void 0;
|
|
4
|
+
exports.SymbolInitialValue = Symbol('Initial Value');
|
|
5
|
+
const errors_1 = require("./errors");
|
|
6
|
+
class FieldConstructor {
|
|
7
|
+
constructor(value) {
|
|
8
|
+
this[exports.SymbolInitialValue] = value;
|
|
9
|
+
}
|
|
10
|
+
get get() {
|
|
11
|
+
const self = this;
|
|
12
|
+
return function () {
|
|
13
|
+
return self[exports.SymbolInitialValue];
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
get set() {
|
|
17
|
+
return function () {
|
|
18
|
+
throw new TypeError(errors_1.ErrorsNames.FORBIDDEN_RE);
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
exports.FieldConstructor = FieldConstructor;
|
|
23
|
+
Object.freeze(FieldConstructor.prototype);
|
|
24
|
+
Object.seal(FieldConstructor.prototype);
|
package/lib/index.d.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
export declare type IDEF<T, P = {}, R = {}> = {
|
|
2
|
-
new (...args: any[]): T;
|
|
3
|
-
(this: T, ...args: any[]): R;
|
|
4
|
-
prototype: P;
|
|
5
|
-
};
|
|
6
1
|
declare const BaseConstructor: ObjectConstructor;
|
|
7
2
|
export declare class BaseClass extends BaseConstructor {
|
|
8
3
|
}
|
|
4
|
+
export declare type IDEF<T, P = {}, R = {}> = {
|
|
5
|
+
new (...args: unknown[]): T;
|
|
6
|
+
(this: T, ...args: unknown[]): R;
|
|
7
|
+
prototype: P;
|
|
8
|
+
};
|
|
9
9
|
export {};
|
package/lib/index.js
CHANGED
|
@@ -1,113 +1,15 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.BaseClass = void 0;
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
MISSING_PROP: 'Attempt to Access to Undefined Prop',
|
|
8
|
-
RIP_FUNCTIONS: 'Functions are Restricted'
|
|
9
|
-
};
|
|
10
|
-
const PRIMITIVE_TYPES = [
|
|
11
|
-
'string',
|
|
12
|
-
'number',
|
|
13
|
-
'boolean',
|
|
14
|
-
];
|
|
15
|
-
const isPrimitive = (value) => {
|
|
16
|
-
return PRIMITIVE_TYPES.includes(typeof value);
|
|
17
|
-
};
|
|
18
|
-
const primitives = (initialValue) => {
|
|
19
|
-
let value = Object(initialValue);
|
|
20
|
-
const initialType = typeof initialValue;
|
|
21
|
-
return {
|
|
22
|
-
get() {
|
|
23
|
-
const proxyAsValue = new Proxy(value, {
|
|
24
|
-
get(_, prop) {
|
|
25
|
-
if (prop === Symbol.toPrimitive) {
|
|
26
|
-
return function (hint) {
|
|
27
|
-
if (hint !== initialType) {
|
|
28
|
-
throw new ReferenceError(ErrorsNames.ACCESS_DENIED);
|
|
29
|
-
}
|
|
30
|
-
return value.valueOf();
|
|
31
|
-
};
|
|
32
|
-
}
|
|
33
|
-
if (prop === 'valueOf') {
|
|
34
|
-
return function () {
|
|
35
|
-
return value.valueOf();
|
|
36
|
-
};
|
|
37
|
-
}
|
|
38
|
-
if (value[prop] instanceof Function) {
|
|
39
|
-
return value[prop].bind(value);
|
|
40
|
-
}
|
|
41
|
-
return value[prop];
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
return proxyAsValue;
|
|
45
|
-
},
|
|
46
|
-
set(replacementValue) {
|
|
47
|
-
if (replacementValue instanceof value.constructor) {
|
|
48
|
-
value = replacementValue;
|
|
49
|
-
return value;
|
|
50
|
-
}
|
|
51
|
-
const prevalue = Object(replacementValue);
|
|
52
|
-
if (prevalue instanceof value.constructor) {
|
|
53
|
-
value = prevalue;
|
|
54
|
-
return value;
|
|
55
|
-
}
|
|
56
|
-
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
57
|
-
throw error;
|
|
58
|
-
}
|
|
59
|
-
};
|
|
60
|
-
};
|
|
61
|
-
const special = (value) => {
|
|
62
|
-
return {
|
|
63
|
-
get() {
|
|
64
|
-
return value;
|
|
65
|
-
},
|
|
66
|
-
set(replacementValue) {
|
|
67
|
-
if (typeof replacementValue === typeof value) {
|
|
68
|
-
value = replacementValue;
|
|
69
|
-
return value;
|
|
70
|
-
}
|
|
71
|
-
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
72
|
-
throw error;
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
};
|
|
76
|
-
const nullish = (value) => {
|
|
77
|
-
return {
|
|
78
|
-
get() {
|
|
79
|
-
return value;
|
|
80
|
-
},
|
|
81
|
-
set() {
|
|
82
|
-
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
83
|
-
throw error;
|
|
84
|
-
}
|
|
85
|
-
};
|
|
86
|
-
};
|
|
87
|
-
const objects = (value) => {
|
|
88
|
-
return {
|
|
89
|
-
get() {
|
|
90
|
-
return value;
|
|
91
|
-
},
|
|
92
|
-
set(replacementValue) {
|
|
93
|
-
if (replacementValue instanceof Object && replacementValue.constructor === value.constructor) {
|
|
94
|
-
value = replacementValue;
|
|
95
|
-
return value;
|
|
96
|
-
}
|
|
97
|
-
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
98
|
-
throw error;
|
|
99
|
-
}
|
|
100
|
-
};
|
|
101
|
-
};
|
|
102
|
-
const functions = () => {
|
|
103
|
-
throw new TypeError(ErrorsNames.RIP_FUNCTIONS);
|
|
104
|
-
};
|
|
4
|
+
const errors_1 = require("./errors");
|
|
5
|
+
const types_1 = require("./types");
|
|
6
|
+
const fields_1 = require("./fields");
|
|
105
7
|
const resolver = Object.entries({
|
|
106
|
-
primitives,
|
|
107
|
-
special,
|
|
108
|
-
nullish,
|
|
109
|
-
objects,
|
|
110
|
-
functions
|
|
8
|
+
primitives: types_1.primitives,
|
|
9
|
+
special: types_1.special,
|
|
10
|
+
nullish: types_1.nullish,
|
|
11
|
+
objects: types_1.objects,
|
|
12
|
+
functions: types_1.functions
|
|
111
13
|
}).reduce((obj, [key, _handler]) => {
|
|
112
14
|
obj[key] = function (initialValue, receiver) {
|
|
113
15
|
const handler = _handler(initialValue);
|
|
@@ -115,14 +17,14 @@ const resolver = Object.entries({
|
|
|
115
17
|
get() {
|
|
116
18
|
const invocationThis = this;
|
|
117
19
|
if (invocationThis !== receiver) {
|
|
118
|
-
throw new ReferenceError(ErrorsNames.ACCESS_DENIED);
|
|
20
|
+
throw new ReferenceError(errors_1.ErrorsNames.ACCESS_DENIED);
|
|
119
21
|
}
|
|
120
22
|
return handler.get();
|
|
121
23
|
},
|
|
122
24
|
set(replacementValue) {
|
|
123
25
|
const invocationThis = this;
|
|
124
26
|
if (invocationThis !== receiver) {
|
|
125
|
-
throw new ReferenceError(ErrorsNames.ACCESS_DENIED);
|
|
27
|
+
throw new ReferenceError(errors_1.ErrorsNames.ACCESS_DENIED);
|
|
126
28
|
}
|
|
127
29
|
return handler.set(replacementValue);
|
|
128
30
|
}
|
|
@@ -132,15 +34,26 @@ const resolver = Object.entries({
|
|
|
132
34
|
}, {});
|
|
133
35
|
const createProperty = (propName, initialValue, receiver) => {
|
|
134
36
|
const value = initialValue;
|
|
135
|
-
const valueIsPrimitive = isPrimitive(initialValue);
|
|
37
|
+
const valueIsPrimitive = (0, types_1.isPrimitive)(initialValue);
|
|
136
38
|
const isObject = typeof initialValue === 'object';
|
|
137
39
|
const isFunction = initialValue instanceof Function;
|
|
138
40
|
const isNull = initialValue === null;
|
|
139
41
|
const type = valueIsPrimitive ? 'primitives' : (isObject ? (isNull ? 'nullish' : 'objects') : (isFunction ? 'functions' : 'special'));
|
|
140
|
-
const descriptor =
|
|
141
|
-
|
|
142
|
-
|
|
42
|
+
const descriptor = (isObject && (value instanceof fields_1.FieldConstructor)) ?
|
|
43
|
+
value
|
|
44
|
+
: Object.assign({ enumerable: true }, resolver[type](value, receiver));
|
|
45
|
+
try {
|
|
46
|
+
const result = Reflect.defineProperty(receiver, propName, descriptor);
|
|
47
|
+
return result;
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
throw error;
|
|
51
|
+
}
|
|
143
52
|
};
|
|
53
|
+
const props2skip = new Set([Symbol.toStringTag, Symbol.iterator]);
|
|
54
|
+
const util = require('util');
|
|
55
|
+
const hasNodeInspect = (util && util.inspect && util.inspect.custom);
|
|
56
|
+
hasNodeInspect && (props2skip.add(util.inspect.custom));
|
|
144
57
|
const handlers = {
|
|
145
58
|
get(target, prop, receiver) {
|
|
146
59
|
const result = Reflect.get(target, prop, receiver);
|
|
@@ -155,7 +68,10 @@ const handlers = {
|
|
|
155
68
|
}, {}));
|
|
156
69
|
};
|
|
157
70
|
}
|
|
158
|
-
|
|
71
|
+
if (props2skip.has(prop)) {
|
|
72
|
+
return undefined;
|
|
73
|
+
}
|
|
74
|
+
throw new Error(`${errors_1.ErrorsNames.MISSING_PROP}: [ ${String(prop).valueOf()} ] of ${receiver.constructor.name}`);
|
|
159
75
|
},
|
|
160
76
|
set(_, prop, value, receiver) {
|
|
161
77
|
const result = createProperty(prop, value, receiver);
|
|
@@ -196,3 +112,9 @@ Object.defineProperty(module.exports, 'BaseClass', {
|
|
|
196
112
|
},
|
|
197
113
|
enumerable: true
|
|
198
114
|
});
|
|
115
|
+
Object.defineProperty(module.exports, 'FieldConstructor', {
|
|
116
|
+
get() {
|
|
117
|
+
return fields_1.FieldConstructor;
|
|
118
|
+
},
|
|
119
|
+
enumerable: true
|
|
120
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const functions: () => never;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.functions = void 0;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
const functions = () => {
|
|
6
|
+
throw new TypeError(errors_1.ErrorsNames.RIP_FUNCTIONS);
|
|
7
|
+
};
|
|
8
|
+
exports.functions = functions;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isPrimitive = exports.special = exports.primitives = exports.objects = exports.nullish = exports.functions = void 0;
|
|
4
|
+
var functions_1 = require("./functions");
|
|
5
|
+
Object.defineProperty(exports, "functions", { enumerable: true, get: function () { return functions_1.functions; } });
|
|
6
|
+
var nullish_1 = require("./nullish");
|
|
7
|
+
Object.defineProperty(exports, "nullish", { enumerable: true, get: function () { return nullish_1.nullish; } });
|
|
8
|
+
var objects_1 = require("./objects");
|
|
9
|
+
Object.defineProperty(exports, "objects", { enumerable: true, get: function () { return objects_1.objects; } });
|
|
10
|
+
var primitives_1 = require("./primitives");
|
|
11
|
+
Object.defineProperty(exports, "primitives", { enumerable: true, get: function () { return primitives_1.primitives; } });
|
|
12
|
+
var special_1 = require("./special");
|
|
13
|
+
Object.defineProperty(exports, "special", { enumerable: true, get: function () { return special_1.special; } });
|
|
14
|
+
const PRIMITIVE_TYPES = [
|
|
15
|
+
'string',
|
|
16
|
+
'number',
|
|
17
|
+
'boolean',
|
|
18
|
+
];
|
|
19
|
+
const isPrimitive = (value) => {
|
|
20
|
+
return PRIMITIVE_TYPES.includes(typeof value);
|
|
21
|
+
};
|
|
22
|
+
exports.isPrimitive = isPrimitive;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.nullish = void 0;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
const nullish = (value) => {
|
|
6
|
+
return {
|
|
7
|
+
get() {
|
|
8
|
+
return value;
|
|
9
|
+
},
|
|
10
|
+
set() {
|
|
11
|
+
const error = new TypeError(errors_1.ErrorsNames.TYPE_MISMATCH);
|
|
12
|
+
throw error;
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
exports.nullish = nullish;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.objects = void 0;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
const objects = (value) => {
|
|
6
|
+
return {
|
|
7
|
+
get() {
|
|
8
|
+
return value;
|
|
9
|
+
},
|
|
10
|
+
set(replacementValue) {
|
|
11
|
+
if (replacementValue instanceof Object && replacementValue.constructor === value.constructor) {
|
|
12
|
+
value = replacementValue;
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
const error = new TypeError(errors_1.ErrorsNames.TYPE_MISMATCH);
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
exports.objects = objects;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.primitives = void 0;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
const primitives = (initialValue) => {
|
|
6
|
+
let value = Object(initialValue);
|
|
7
|
+
const initialType = typeof initialValue;
|
|
8
|
+
return {
|
|
9
|
+
get() {
|
|
10
|
+
const proxyAsValue = new Proxy(value, {
|
|
11
|
+
get(_, prop) {
|
|
12
|
+
if (prop === Symbol.toPrimitive) {
|
|
13
|
+
return function (hint) {
|
|
14
|
+
if (hint !== initialType) {
|
|
15
|
+
throw new ReferenceError(errors_1.ErrorsNames.ACCESS_DENIED);
|
|
16
|
+
}
|
|
17
|
+
return value.valueOf();
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
if (prop === 'valueOf') {
|
|
21
|
+
return function () {
|
|
22
|
+
return value.valueOf();
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
if (value[prop] instanceof Function) {
|
|
26
|
+
return value[prop].bind(value);
|
|
27
|
+
}
|
|
28
|
+
return value[prop];
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
return proxyAsValue;
|
|
32
|
+
},
|
|
33
|
+
set(replacementValue) {
|
|
34
|
+
if (replacementValue instanceof value.constructor) {
|
|
35
|
+
value = replacementValue;
|
|
36
|
+
return value;
|
|
37
|
+
}
|
|
38
|
+
const prevalue = Object(replacementValue);
|
|
39
|
+
if (prevalue instanceof value.constructor) {
|
|
40
|
+
value = prevalue;
|
|
41
|
+
return value;
|
|
42
|
+
}
|
|
43
|
+
const error = new TypeError(errors_1.ErrorsNames.TYPE_MISMATCH);
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
};
|
|
48
|
+
exports.primitives = primitives;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.special = void 0;
|
|
4
|
+
const errors_1 = require("../errors");
|
|
5
|
+
const special = (value) => {
|
|
6
|
+
return {
|
|
7
|
+
get() {
|
|
8
|
+
return value;
|
|
9
|
+
},
|
|
10
|
+
set(replacementValue) {
|
|
11
|
+
if (typeof replacementValue === typeof value) {
|
|
12
|
+
value = replacementValue;
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
const error = new TypeError(errors_1.ErrorsNames.TYPE_MISMATCH);
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
};
|
|
20
|
+
exports.special = special;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "typeomatica",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.8",
|
|
4
4
|
"description": "type logic against javascript metaprogramming",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
"lint": "npx eslint --fix --ignore-path .gitignore ./lib",
|
|
10
10
|
"test": "npx jest",
|
|
11
11
|
"test:cov": "npx jest --collectCoverage",
|
|
12
|
-
"debug": "
|
|
12
|
+
"debug": "node --inspect-brk ./node_modules/jest/bin/jest.js --runInBand",
|
|
13
|
+
"debug:old": "npx --node-options=--inspect-brk jest",
|
|
14
|
+
"prepare": "husky install"
|
|
13
15
|
},
|
|
14
16
|
"keywords": [
|
|
15
17
|
"strict",
|
|
@@ -25,16 +27,16 @@
|
|
|
25
27
|
"url": "git+https://github.com/wentout/typeomatica.git"
|
|
26
28
|
},
|
|
27
29
|
"devDependencies": {
|
|
28
|
-
"@types/jest": "^
|
|
29
|
-
"@types/node": "^
|
|
30
|
-
"eslint": "^7.
|
|
31
|
-
"husky": "^
|
|
32
|
-
"jest": "^
|
|
33
|
-
"lint-staged": "^
|
|
34
|
-
"
|
|
35
|
-
"ts-
|
|
30
|
+
"@types/jest": "^27.0.1",
|
|
31
|
+
"@types/node": "^16.9.2",
|
|
32
|
+
"eslint": "^7.32.0",
|
|
33
|
+
"husky": "^8.0.0",
|
|
34
|
+
"jest": "^27.2.0",
|
|
35
|
+
"lint-staged": "^11.1.2",
|
|
36
|
+
"set-value": "^4.1.0",
|
|
37
|
+
"ts-jest": "^27.0.5",
|
|
38
|
+
"ts-node": "^10.2.1",
|
|
36
39
|
"tslint": "^6.1.3",
|
|
37
|
-
"typescript": "^4.
|
|
38
|
-
"typologica": "0.0.77"
|
|
40
|
+
"typescript": "^4.4.3"
|
|
39
41
|
}
|
|
40
42
|
}
|
package/src/errors.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
export const ErrorsNames = {
|
|
4
|
+
TYPE_MISMATCH: 'Type Mismatch',
|
|
5
|
+
ACCESS_DENIED: 'Value Access Denied',
|
|
6
|
+
MISSING_PROP: 'Attempt to Access to Undefined Prop',
|
|
7
|
+
RIP_FUNCTIONS: 'Functions are Restricted',
|
|
8
|
+
FORBIDDEN_RE: 'Re-Assirnment is Forbidden'
|
|
9
|
+
};
|
package/src/fields.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
export const SymbolInitialValue = Symbol('Initial Value');
|
|
4
|
+
|
|
5
|
+
import { ErrorsNames } from './errors'
|
|
6
|
+
|
|
7
|
+
interface FieldDefinition {
|
|
8
|
+
[SymbolInitialValue]: unknown
|
|
9
|
+
// get?: unknown
|
|
10
|
+
// set?: unknown
|
|
11
|
+
// configurable: boolean,
|
|
12
|
+
// enumerable: boolean,
|
|
13
|
+
// writable: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// export const FieldConstructor = function (this: FieldDefinition, value: unknown) {
|
|
17
|
+
// this[SymbolInitialValue] = value;
|
|
18
|
+
// } as ObjectConstructor;
|
|
19
|
+
|
|
20
|
+
export class FieldConstructor implements FieldDefinition {
|
|
21
|
+
[SymbolInitialValue]: unknown
|
|
22
|
+
public get get () {
|
|
23
|
+
const self = this;
|
|
24
|
+
return function (this: FieldDefinition) {
|
|
25
|
+
return self[SymbolInitialValue];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
public get set () {
|
|
29
|
+
return function () {
|
|
30
|
+
throw new TypeError(ErrorsNames.FORBIDDEN_RE);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
constructor (value: unknown) {
|
|
34
|
+
this[SymbolInitialValue] = value;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Object.assign(FieldConstructor.prototype, {
|
|
39
|
+
// configurable: false,
|
|
40
|
+
// enumerable: false,
|
|
41
|
+
// // writable: false
|
|
42
|
+
// })
|
|
43
|
+
|
|
44
|
+
// Object.defineProperty(FieldConstructor.prototype, 'get', {
|
|
45
|
+
// get() {
|
|
46
|
+
// return this[symbolValue];
|
|
47
|
+
// },
|
|
48
|
+
// // @ts-ignore
|
|
49
|
+
// set(value: unknown) {
|
|
50
|
+
// throw new Error('broken behaviour: assignment to getter');
|
|
51
|
+
// },
|
|
52
|
+
// configurable: false,
|
|
53
|
+
// enumerable: true,
|
|
54
|
+
// // writable: false
|
|
55
|
+
// });
|
|
56
|
+
|
|
57
|
+
// Object.defineProperty(FieldConstructor.prototype, 'set', {
|
|
58
|
+
// get() {
|
|
59
|
+
// return function (this: FieldDefinition, value: unknown) {
|
|
60
|
+
// this[symbolValue] = value;
|
|
61
|
+
// }
|
|
62
|
+
// },
|
|
63
|
+
// // @ts-ignore
|
|
64
|
+
// set(value: unknown) {
|
|
65
|
+
// throw new Error('broken behaviour: assignment to setter');
|
|
66
|
+
// },
|
|
67
|
+
// configurable: false,
|
|
68
|
+
// enumerable: true,
|
|
69
|
+
// // writable: false
|
|
70
|
+
// });
|
|
71
|
+
|
|
72
|
+
Object.freeze(FieldConstructor.prototype);
|
|
73
|
+
Object.seal(FieldConstructor.prototype);
|
package/src/index.ts
CHANGED
|
@@ -1,136 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
TYPE_MISMATCH: 'Type Mismatch',
|
|
5
|
-
ACCESS_DENIED: 'Value Access Denied',
|
|
6
|
-
MISSING_PROP: 'Attempt to Access to Undefined Prop',
|
|
7
|
-
RIP_FUNCTIONS: 'Functions are Restricted'
|
|
8
|
-
};
|
|
9
|
-
|
|
10
|
-
const PRIMITIVE_TYPES = [
|
|
11
|
-
'string',
|
|
12
|
-
'number',
|
|
13
|
-
'boolean',
|
|
14
|
-
];
|
|
15
|
-
|
|
16
|
-
const isPrimitive = (value: unknown) => {
|
|
17
|
-
return PRIMITIVE_TYPES.includes(typeof value);
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
const primitives = (initialValue: object) => {
|
|
21
|
-
let value = Object(initialValue);
|
|
22
|
-
const initialType = typeof initialValue;
|
|
23
|
-
|
|
24
|
-
return {
|
|
25
|
-
get() {
|
|
26
|
-
const proxyAsValue = new Proxy(value, {
|
|
27
|
-
// get(target, prop, receiver) {
|
|
28
|
-
get(_, prop) {
|
|
29
|
-
|
|
30
|
-
if (prop === Symbol.toPrimitive) {
|
|
31
|
-
return function (hint: string) {
|
|
32
|
-
if (hint !== initialType) {
|
|
33
|
-
throw new ReferenceError(ErrorsNames.ACCESS_DENIED);
|
|
34
|
-
}
|
|
35
|
-
return value.valueOf();
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
if (prop === 'valueOf') {
|
|
40
|
-
return function () {
|
|
41
|
-
return value.valueOf();
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
// @ts-ignore
|
|
46
|
-
if (value[prop] instanceof Function) {
|
|
47
|
-
return value[prop].bind(value);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return value[prop];
|
|
51
|
-
}
|
|
52
|
-
});
|
|
53
|
-
return proxyAsValue;
|
|
54
|
-
},
|
|
55
|
-
// get() {
|
|
56
|
-
// const preparedValue = {
|
|
57
|
-
// [Symbol.toPrimitive]() {
|
|
58
|
-
// return function () {
|
|
59
|
-
// throw new ReferenceError(ErrorsNames.ACCESS_DENIED);
|
|
60
|
-
// };
|
|
61
|
-
// }
|
|
62
|
-
// };
|
|
63
|
-
// Reflect.setPrototypeOf(preparedValue, value);
|
|
64
|
-
// debugger;
|
|
65
|
-
// return preparedValue;
|
|
66
|
-
// },
|
|
67
|
-
set(replacementValue: unknown) {
|
|
68
|
-
if (replacementValue instanceof value.constructor) {
|
|
69
|
-
value = replacementValue;
|
|
70
|
-
return value;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const prevalue = Object(replacementValue);
|
|
74
|
-
|
|
75
|
-
if (prevalue instanceof value.constructor) {
|
|
76
|
-
value = prevalue;
|
|
77
|
-
return value;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
81
|
-
throw error;
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
const special = (value: object) => {
|
|
87
|
-
return {
|
|
88
|
-
get() {
|
|
89
|
-
return value;
|
|
90
|
-
},
|
|
91
|
-
set(replacementValue: object) {
|
|
92
|
-
if (typeof replacementValue === typeof value) {
|
|
93
|
-
value = replacementValue;
|
|
94
|
-
return value;
|
|
95
|
-
|
|
96
|
-
}
|
|
97
|
-
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
98
|
-
throw error;
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
const nullish = (value: object) => {
|
|
104
|
-
return {
|
|
105
|
-
get() {
|
|
106
|
-
return value;
|
|
107
|
-
},
|
|
108
|
-
set() {
|
|
109
|
-
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
110
|
-
throw error;
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
};
|
|
3
|
+
import { ErrorsNames } from './errors';
|
|
114
4
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
return value;
|
|
124
|
-
}
|
|
125
|
-
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
126
|
-
throw error;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
};
|
|
5
|
+
import {
|
|
6
|
+
functions,
|
|
7
|
+
nullish,
|
|
8
|
+
objects,
|
|
9
|
+
primitives,
|
|
10
|
+
special,
|
|
11
|
+
isPrimitive
|
|
12
|
+
} from './types';
|
|
130
13
|
|
|
131
|
-
|
|
132
|
-
throw new TypeError(ErrorsNames.RIP_FUNCTIONS);
|
|
133
|
-
};
|
|
14
|
+
import { FieldConstructor } from './fields';
|
|
134
15
|
|
|
135
16
|
const resolver = Object.entries({
|
|
136
17
|
primitives,
|
|
@@ -163,7 +44,8 @@ const resolver = Object.entries({
|
|
|
163
44
|
return obj;
|
|
164
45
|
}, {});
|
|
165
46
|
|
|
166
|
-
|
|
47
|
+
|
|
48
|
+
const createProperty = (propName: string, initialValue: unknown, receiver: object) => {
|
|
167
49
|
|
|
168
50
|
const value = initialValue;
|
|
169
51
|
const valueIsPrimitive = isPrimitive(initialValue);
|
|
@@ -171,23 +53,47 @@ const createProperty = (propName: string, initialValue: any, receiver: object) =
|
|
|
171
53
|
const isFunction = initialValue instanceof Function;
|
|
172
54
|
const isNull = initialValue === null;
|
|
173
55
|
|
|
56
|
+
/**
|
|
57
|
+
* special: undefined or BigInt or Symbol
|
|
58
|
+
* or other non constructible type
|
|
59
|
+
*/
|
|
60
|
+
|
|
174
61
|
const type = valueIsPrimitive ? 'primitives' : (
|
|
175
62
|
isObject ? (
|
|
176
63
|
isNull ? 'nullish' : 'objects'
|
|
177
64
|
) : (
|
|
178
|
-
isFunction ? 'functions' : 'special'
|
|
65
|
+
isFunction ? 'functions' : 'special'
|
|
66
|
+
)
|
|
67
|
+
);
|
|
68
|
+
|
|
69
|
+
const descriptor = (isObject && (value instanceof FieldConstructor)) ?
|
|
70
|
+
value
|
|
71
|
+
:
|
|
72
|
+
{
|
|
73
|
+
enumerable: true,
|
|
74
|
+
// @ts-ignore
|
|
75
|
+
...resolver[type](value, receiver),
|
|
76
|
+
};
|
|
179
77
|
|
|
180
|
-
//
|
|
181
|
-
|
|
78
|
+
// if (value instanceof FieldConstructor) {
|
|
79
|
+
// descriptor;
|
|
80
|
+
// debugger;
|
|
81
|
+
// }
|
|
182
82
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
})
|
|
83
|
+
try {
|
|
84
|
+
const result = Reflect.defineProperty(receiver, propName, descriptor);
|
|
85
|
+
return result;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
// debugger;
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
187
90
|
|
|
188
|
-
return result;
|
|
189
91
|
};
|
|
190
92
|
|
|
93
|
+
const props2skip = new Set([Symbol.toStringTag, Symbol.iterator]);
|
|
94
|
+
const util = require('util');
|
|
95
|
+
const hasNodeInspect = (util && util.inspect && util.inspect.custom);
|
|
96
|
+
hasNodeInspect && (props2skip.add(util.inspect.custom));
|
|
191
97
|
|
|
192
98
|
const handlers = {
|
|
193
99
|
get(target: object, prop: string | symbol, receiver: object) {
|
|
@@ -204,7 +110,11 @@ const handlers = {
|
|
|
204
110
|
}, {}));
|
|
205
111
|
}
|
|
206
112
|
}
|
|
207
|
-
|
|
113
|
+
// @ts-ignore
|
|
114
|
+
if (props2skip.has(prop)) {
|
|
115
|
+
return undefined;
|
|
116
|
+
}
|
|
117
|
+
throw new Error(`${ErrorsNames.MISSING_PROP}: [ ${String(prop).valueOf()} ] of ${receiver.constructor.name}`);
|
|
208
118
|
},
|
|
209
119
|
set(_: object, prop: string, value: unknown, receiver: object) {
|
|
210
120
|
const result = createProperty(prop, value, receiver);
|
|
@@ -221,12 +131,6 @@ const BaseTarget = Object.create(null);
|
|
|
221
131
|
|
|
222
132
|
// const BasePrototype = new Proxy(BaseTarget, handlers);
|
|
223
133
|
|
|
224
|
-
export type IDEF<T, P = {}, R = {}> = {
|
|
225
|
-
new(...args: any[]): T;
|
|
226
|
-
(this: T, ...args: any[]): R;
|
|
227
|
-
prototype: P;
|
|
228
|
-
};
|
|
229
|
-
|
|
230
134
|
// @ts-ignore
|
|
231
135
|
const BaseConstructor = function (this: object, InstanceTarget = BaseTarget) {
|
|
232
136
|
if (!new.target) {
|
|
@@ -261,10 +165,24 @@ Object.defineProperty(module, 'exports', {
|
|
|
261
165
|
|
|
262
166
|
// @ts-ignore
|
|
263
167
|
export class BaseClass extends BaseConstructor { };
|
|
168
|
+
// export { FieldConstructor } from './fields';
|
|
264
169
|
|
|
265
170
|
Object.defineProperty(module.exports, 'BaseClass', {
|
|
266
171
|
get() {
|
|
267
172
|
return BaseClass;
|
|
268
173
|
},
|
|
269
174
|
enumerable: true
|
|
270
|
-
});
|
|
175
|
+
});
|
|
176
|
+
Object.defineProperty(module.exports, 'FieldConstructor', {
|
|
177
|
+
get() {
|
|
178
|
+
return FieldConstructor;
|
|
179
|
+
},
|
|
180
|
+
enumerable: true
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
export type IDEF<T, P = {}, R = {}> = {
|
|
185
|
+
new(...args: unknown[]): T;
|
|
186
|
+
(this: T, ...args: unknown[]): R;
|
|
187
|
+
prototype: P;
|
|
188
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
export { functions } from './functions';
|
|
4
|
+
export { nullish } from './nullish';
|
|
5
|
+
export { objects } from './objects';
|
|
6
|
+
export { primitives } from './primitives';
|
|
7
|
+
export { special } from './special';
|
|
8
|
+
|
|
9
|
+
const PRIMITIVE_TYPES = [
|
|
10
|
+
'string',
|
|
11
|
+
'number',
|
|
12
|
+
'boolean',
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
export const isPrimitive = (value: unknown) => {
|
|
16
|
+
return PRIMITIVE_TYPES.includes(typeof value);
|
|
17
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import { ErrorsNames } from '../errors';
|
|
4
|
+
|
|
5
|
+
export const objects = (value: object) => {
|
|
6
|
+
return {
|
|
7
|
+
get() {
|
|
8
|
+
return value;
|
|
9
|
+
},
|
|
10
|
+
set(replacementValue: unknown) {
|
|
11
|
+
if (replacementValue instanceof Object && replacementValue.constructor === value.constructor) {
|
|
12
|
+
value = replacementValue;
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import { ErrorsNames } from '../errors';
|
|
4
|
+
|
|
5
|
+
export const primitives = (initialValue: object) => {
|
|
6
|
+
let value = Object(initialValue);
|
|
7
|
+
const initialType = typeof initialValue;
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
get() {
|
|
11
|
+
const proxyAsValue = new Proxy(value, {
|
|
12
|
+
// get(target, prop, receiver) {
|
|
13
|
+
get(_, prop) {
|
|
14
|
+
if (prop === Symbol.toPrimitive) {
|
|
15
|
+
return function (hint: string) {
|
|
16
|
+
if (hint !== initialType) {
|
|
17
|
+
throw new ReferenceError(ErrorsNames.ACCESS_DENIED);
|
|
18
|
+
}
|
|
19
|
+
return value.valueOf();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (prop === 'valueOf') {
|
|
24
|
+
return function () {
|
|
25
|
+
return value.valueOf();
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// @ts-ignore
|
|
30
|
+
if (value[prop] instanceof Function) {
|
|
31
|
+
return value[prop].bind(value);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return value[prop];
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
return proxyAsValue;
|
|
38
|
+
},
|
|
39
|
+
// get() {
|
|
40
|
+
// const preparedValue = {
|
|
41
|
+
// [Symbol.toPrimitive]() {
|
|
42
|
+
// return function () {
|
|
43
|
+
// throw new ReferenceError(ErrorsNames.ACCESS_DENIED);
|
|
44
|
+
// };
|
|
45
|
+
// }
|
|
46
|
+
// };
|
|
47
|
+
// Reflect.setPrototypeOf(preparedValue, value);
|
|
48
|
+
// debugger;
|
|
49
|
+
// return preparedValue;
|
|
50
|
+
// },
|
|
51
|
+
set(replacementValue: unknown) {
|
|
52
|
+
if (replacementValue instanceof value.constructor) {
|
|
53
|
+
value = replacementValue;
|
|
54
|
+
return value;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const prevalue = Object(replacementValue);
|
|
58
|
+
|
|
59
|
+
if (prevalue instanceof value.constructor) {
|
|
60
|
+
value = prevalue;
|
|
61
|
+
return value;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
65
|
+
throw error;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
import { ErrorsNames } from '../errors';
|
|
4
|
+
|
|
5
|
+
export const special = (value: object) => {
|
|
6
|
+
return {
|
|
7
|
+
get() {
|
|
8
|
+
return value;
|
|
9
|
+
},
|
|
10
|
+
set(replacementValue: object) {
|
|
11
|
+
if (typeof replacementValue === typeof value) {
|
|
12
|
+
value = replacementValue;
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
const error = new TypeError(ErrorsNames.TYPE_MISMATCH);
|
|
16
|
+
throw error;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
};
|
package/test/index.ts
CHANGED
|
@@ -5,7 +5,10 @@
|
|
|
5
5
|
// or meaningless
|
|
6
6
|
|
|
7
7
|
const BasePrototype = require('..');
|
|
8
|
-
|
|
8
|
+
// @ts-ignore
|
|
9
|
+
import { BaseClass, IDEF, FieldConstructor } from '..';
|
|
10
|
+
|
|
11
|
+
debugger;
|
|
9
12
|
|
|
10
13
|
class Base extends BasePrototype({
|
|
11
14
|
additionalProp: 321,
|
|
@@ -14,14 +17,22 @@ class Base extends BasePrototype({
|
|
|
14
17
|
},
|
|
15
18
|
}) {
|
|
16
19
|
numberValue = 123;
|
|
20
|
+
|
|
21
|
+
get getterField() {
|
|
22
|
+
return '123';
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
set setterField(value: string) {
|
|
26
|
+
this.stringValue = value;
|
|
27
|
+
}
|
|
28
|
+
|
|
17
29
|
constructor() {
|
|
18
30
|
super();
|
|
19
31
|
this.stringValue = '123';
|
|
20
32
|
this.booleanValue = true;
|
|
21
33
|
this.objectValue = {};
|
|
22
34
|
}
|
|
23
|
-
}
|
|
24
|
-
|
|
35
|
+
}
|
|
25
36
|
const baseInstance = new Base;
|
|
26
37
|
|
|
27
38
|
const upperInstance = Object.create(baseInstance);
|
|
@@ -29,7 +40,6 @@ const upperInstance = Object.create(baseInstance);
|
|
|
29
40
|
class SimpleBase extends BaseClass {
|
|
30
41
|
stringProp = '123';
|
|
31
42
|
};
|
|
32
|
-
|
|
33
43
|
const simpleInstance = new SimpleBase;
|
|
34
44
|
|
|
35
45
|
type MyFunctionalConstructorInstance = {
|
|
@@ -37,6 +47,7 @@ type MyFunctionalConstructorInstance = {
|
|
|
37
47
|
};
|
|
38
48
|
|
|
39
49
|
const MyFunctionalConstructor = function () {
|
|
50
|
+
// @ts-ignore
|
|
40
51
|
this.stringProp = '123';
|
|
41
52
|
} as IDEF<MyFunctionalConstructorInstance>;
|
|
42
53
|
|
|
@@ -44,7 +55,8 @@ Reflect.setPrototypeOf(MyFunctionalConstructor.prototype, new BasePrototype);
|
|
|
44
55
|
|
|
45
56
|
const myFunctionalInstance = new MyFunctionalConstructor();
|
|
46
57
|
|
|
47
|
-
class
|
|
58
|
+
class SecondaryExtend extends Base { second = 123 };
|
|
59
|
+
class TripleExtend extends SecondaryExtend { };
|
|
48
60
|
const tiripleExtendInstance = new TripleExtend;
|
|
49
61
|
|
|
50
62
|
class NetworkedExtention extends BasePrototype(tiripleExtendInstance) { };
|
|
@@ -59,6 +71,84 @@ const extendedSetInstance = new ExtendedSet;
|
|
|
59
71
|
|
|
60
72
|
const MUTATION_VALUE = -2;
|
|
61
73
|
|
|
74
|
+
|
|
75
|
+
class MyFieldConstructorNoRe extends FieldConstructor {
|
|
76
|
+
_value: string
|
|
77
|
+
constructor(value: string) {
|
|
78
|
+
super(value);
|
|
79
|
+
Reflect.defineProperty(this, 'enumerable', {
|
|
80
|
+
value: true
|
|
81
|
+
});
|
|
82
|
+
this._value = value;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
class MyFieldConstructorReGet extends MyFieldConstructorNoRe {
|
|
86
|
+
constructor(value: string) {
|
|
87
|
+
super(value);
|
|
88
|
+
const self = this;
|
|
89
|
+
Reflect.defineProperty(this, 'enumerable', {
|
|
90
|
+
value: true
|
|
91
|
+
});
|
|
92
|
+
Reflect.defineProperty(this, 'get', {
|
|
93
|
+
get() {
|
|
94
|
+
return function () {
|
|
95
|
+
return self._value;
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
enumerable: true
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
class MyFieldConstructorReSet extends MyFieldConstructorNoRe {
|
|
103
|
+
constructor(value: string) {
|
|
104
|
+
super(value);
|
|
105
|
+
const self = this;
|
|
106
|
+
Reflect.defineProperty(this, 'enumerable', {
|
|
107
|
+
value: true
|
|
108
|
+
});
|
|
109
|
+
Reflect.defineProperty(this, 'set', {
|
|
110
|
+
get() {
|
|
111
|
+
return function (value: string) {
|
|
112
|
+
self._value = value;
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
enumerable: true
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
class MyFieldConstructor extends MyFieldConstructorReGet {
|
|
120
|
+
constructor(value: string) {
|
|
121
|
+
super(value);
|
|
122
|
+
const self = this;
|
|
123
|
+
Reflect.defineProperty(this, 'enumerable', {
|
|
124
|
+
value: true
|
|
125
|
+
});
|
|
126
|
+
Reflect.defineProperty(this, 'set', {
|
|
127
|
+
get() {
|
|
128
|
+
return function (value: string) {
|
|
129
|
+
self._value = value;
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
enumerable: true
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const myField = new MyFieldConstructor('initial value');
|
|
138
|
+
const myFieldReGet = new MyFieldConstructorReGet('initial value for get check');
|
|
139
|
+
const myFieldReSet = new MyFieldConstructorReSet('initial value for set check');
|
|
140
|
+
|
|
141
|
+
class MadeFieldClass extends BaseClass { myField = myField as unknown | string };
|
|
142
|
+
class SecondMadeFieldClass extends BaseClass { myField = myField as unknown | string };
|
|
143
|
+
const madeFieldInstance = new MadeFieldClass;
|
|
144
|
+
const secondMadeFieldInstance = new MadeFieldClass;
|
|
145
|
+
const thirdMadeFieldInstance = new SecondMadeFieldClass;
|
|
146
|
+
|
|
147
|
+
class MadeReGet extends BaseClass { myField = myFieldReGet as unknown | string }
|
|
148
|
+
class MadeReSet extends BaseClass { myField = myFieldReSet as unknown | string }
|
|
149
|
+
const madeReGet = new MadeReGet;
|
|
150
|
+
const madeReSet = new MadeReSet;
|
|
151
|
+
|
|
62
152
|
describe('props tests', () => {
|
|
63
153
|
|
|
64
154
|
test('base instance has props', () => {
|
|
@@ -127,6 +217,62 @@ describe('props tests', () => {
|
|
|
127
217
|
expect(baseInstance.objectValue.a).toEqual(123);
|
|
128
218
|
});
|
|
129
219
|
|
|
220
|
+
test('correct custom field creation', () => {
|
|
221
|
+
expect(madeFieldInstance.myField).toEqual('initial value');
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
test('correct custom field assignment', () => {
|
|
225
|
+
madeFieldInstance.myField = 'replaced';
|
|
226
|
+
expect(secondMadeFieldInstance.myField).toEqual('replaced');
|
|
227
|
+
expect(thirdMadeFieldInstance.myField).toEqual('replaced');
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test('correct custom field no-re-assignment', () => {
|
|
231
|
+
expect(madeReGet.myField).toEqual('initial value for get check');
|
|
232
|
+
expect(() => {
|
|
233
|
+
|
|
234
|
+
madeReGet.myField = 'replaced';
|
|
235
|
+
|
|
236
|
+
}).toThrow(new TypeError('Re-Assirnment is Forbidden'));
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
test('correct custom field setter only', () => {
|
|
240
|
+
madeReSet.myField = 'replaced';
|
|
241
|
+
expect(madeReSet.myField).toEqual('initial value for set check');
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
test('takes error on wrong field definition', () => {
|
|
245
|
+
expect(() => {
|
|
246
|
+
class WrongFieldConstructor extends FieldConstructor {
|
|
247
|
+
value: number
|
|
248
|
+
constructor(value: number) {
|
|
249
|
+
super(value)
|
|
250
|
+
this.value = value;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
const wrongField = new WrongFieldConstructor(123);
|
|
254
|
+
class WithWrongField extends BaseClass {
|
|
255
|
+
erroredField = wrongField
|
|
256
|
+
}
|
|
257
|
+
new WithWrongField;
|
|
258
|
+
|
|
259
|
+
}).toThrow();
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
test('correct custom missing prop search creation', () => {
|
|
263
|
+
// @ts-ignore
|
|
264
|
+
expect(madeFieldInstance[Symbol.toStringTag]).toEqual(undefined);
|
|
265
|
+
// @ts-ignore
|
|
266
|
+
expect(madeFieldInstance[Symbol.iterator]).toEqual(undefined);
|
|
267
|
+
const util = require('util');
|
|
268
|
+
// @ts-ignore
|
|
269
|
+
expect(madeFieldInstance[util.inspect.custom]).toEqual(undefined);
|
|
270
|
+
// @ts-ignore
|
|
271
|
+
const inspected = util.inspect(madeFieldInstance);
|
|
272
|
+
const expected = 'MadeFieldClass { myField: [Getter/Setter] }';
|
|
273
|
+
expect(expected).toEqual(expected);
|
|
274
|
+
});
|
|
275
|
+
|
|
130
276
|
test('wrong assignment to objects', () => {
|
|
131
277
|
|
|
132
278
|
expect(() => {
|
|
@@ -249,7 +395,7 @@ describe('props tests', () => {
|
|
|
249
395
|
|
|
250
396
|
baseInstance.missingValue > 1;
|
|
251
397
|
|
|
252
|
-
}).toThrow(new TypeError('Attempt to Access to Undefined Prop: [ missingValue ]'));
|
|
398
|
+
}).toThrow(new TypeError('Attempt to Access to Undefined Prop: [ missingValue ] of Base'));
|
|
253
399
|
});
|
|
254
400
|
|
|
255
401
|
});
|
|
@@ -368,9 +514,10 @@ describe('deep extend works', () => {
|
|
|
368
514
|
test('class extended three times construction', () => {
|
|
369
515
|
|
|
370
516
|
expect(tiripleExtendInstance).toBeInstanceOf(Base);
|
|
371
|
-
expect(tiripleExtendInstance).toBeInstanceOf(
|
|
517
|
+
expect(tiripleExtendInstance).toBeInstanceOf(SecondaryExtend);
|
|
372
518
|
expect(tiripleExtendInstance).toBeInstanceOf(TripleExtend);
|
|
373
519
|
expect(`${tiripleExtendInstance.stringValue}`).toEqual('123');
|
|
520
|
+
expect(tiripleExtendInstance.second).toBeInstanceOf(Number);
|
|
374
521
|
|
|
375
522
|
});
|
|
376
523
|
|