Browse code
feat(primitives): handle all primitives correctly
Edward authored on 10/01/2022 08:48:11
Showing 2 changed files
Showing 2 changed files
... | ... |
@@ -16,6 +16,20 @@ export const NoNextMethodError = SubTypeError("NoNextMethodError"); |
16 | 16 |
export const NoApplicableMethodError = SubTypeError("NoApplicableMethodError"); |
17 | 17 |
export const NoPrimaryMethodError = SubTypeError("NoPrimaryMethodError"); |
18 | 18 |
|
19 |
+export class UnhandledObjType extends Error { |
|
20 |
+ /** |
|
21 |
+ * @param {string} objType |
|
22 |
+ */ |
|
23 |
+ constructor(objType, ...params) { |
|
24 |
+ super(...params); |
|
25 |
+ this.objType = objType; |
|
26 |
+ } |
|
27 |
+ |
|
28 |
+ toString() { |
|
29 |
+ return `[${this.name}: unhandled objType: ${this.objType}]`; |
|
30 |
+ } |
|
31 |
+} |
|
32 |
+ |
|
19 | 33 |
const before_qualifier = Symbol.for("before"); |
20 | 34 |
const after_qualifier = Symbol.for("after"); |
21 | 35 |
const around_qualifier = Symbol.for("around"); |
... | ... |
@@ -267,16 +281,24 @@ export function matches_specializer(obj, specializer) { |
267 | 281 |
if (obj === null && obj === specializer) { |
268 | 282 |
result = true; |
269 | 283 |
} else if (specializer && specializer.prototype !== undefined) { |
270 |
- if (!result && objType === "object") { |
|
271 |
- result = Object.isPrototypeOf.call(specializer_proto, obj); |
|
284 |
+ if (objType === "object") { |
|
285 |
+ if (!result) { |
|
286 |
+ result = Object.isPrototypeOf.call(specializer_proto, obj); |
|
287 |
+ } |
|
272 | 288 |
} else if (objType === "number") { |
273 |
- result = |
|
274 |
- matches_specializer(Number.prototype, specializer) || |
|
275 |
- matches_specializer(specializer_proto, Number); |
|
289 |
+ result = matches_specializer(Number.prototype, specializer); |
|
290 |
+ } else if (objType === "boolean") { |
|
291 |
+ result = matches_specializer(Boolean.prototype, specializer); |
|
276 | 292 |
} else if (objType === "string") { |
277 |
- result = |
|
278 |
- matches_specializer(String.prototype, specializer) || |
|
279 |
- matches_specializer(specializer_proto, String); |
|
293 |
+ result = matches_specializer(String.prototype, specializer); |
|
294 |
+ } else if (objType === "symbol") { |
|
295 |
+ result = matches_specializer(Symbol.prototype, specializer); |
|
296 |
+ } else if (objType === "undefined") { |
|
297 |
+ result = obj === specializer; |
|
298 |
+ } else if (objType === "bigint") { |
|
299 |
+ result = matches_specializer(BigInt.prototype, specializer); |
|
300 |
+ } else { |
|
301 |
+ throw new UnhandledObjType(objType); |
|
280 | 302 |
} |
281 | 303 |
} else if (specializer instanceof Specializer) { |
282 | 304 |
result = specializer.matches(obj); |
... | ... |
@@ -46,43 +46,81 @@ describe("matches_specializer", () => { |
46 | 46 |
).toBeFalsy(); |
47 | 47 |
}); |
48 | 48 |
|
49 |
+ describe("primitives", () => { |
|
50 |
+ test("null behavior", () => { |
|
51 |
+ expect(uut.matches_specializer(null, null)).toBeTruthy(); |
|
52 |
+ expect(uut.matches_specializer(null, Number)).toBeFalsy(); |
|
53 |
+ expect(uut.matches_specializer(null, String)).toBeFalsy(); |
|
54 |
+ expect(uut.matches_specializer(null, Object)).toBeFalsy(); |
|
55 |
+ }); |
|
56 |
+ |
|
57 |
+ test("undefined (the value) behavior", () => { |
|
58 |
+ expect(uut.matches_specializer(undefined, undefined)).toBeTruthy(); |
|
59 |
+ expect(uut.matches_specializer(undefined, Number)).toBeFalsy(); |
|
60 |
+ expect(uut.matches_specializer(undefined, String)).toBeFalsy(); |
|
61 |
+ expect(uut.matches_specializer(undefined, Object)).toBeFalsy(); |
|
62 |
+ }); |
|
63 |
+ |
|
64 |
+ test.each([true, false])("booleans -> %s", bool => { |
|
65 |
+ expect(bool).not.toBe(Object(bool)); |
|
66 |
+ expect(uut.matches_specializer(new Boolean(bool), Boolean)).toBeTruthy(); |
|
67 |
+ expect(uut.matches_specializer(bool, Boolean)).toBeTruthy(); |
|
68 |
+ expect(uut.matches_specializer(bool, Object)).toBeTruthy(); |
|
69 |
+ }); |
|
70 |
+ |
|
71 |
+ test("works for numbers", () => { |
|
72 |
+ expect(1).not.toBe(Object(1)); |
|
73 |
+ expect(uut.matches_specializer(new Number(1), Number)).toBeTruthy(); |
|
74 |
+ expect(uut.matches_specializer(new Number(1), Object)).toBeTruthy(); |
|
75 |
+ expect(uut.matches_specializer(new Number(1), String)).toBeFalsy(); |
|
76 |
+ |
|
77 |
+ expect(uut.matches_specializer(1, Number)).toBeTruthy(); |
|
78 |
+ expect(uut.matches_specializer(1, Object)).toBeTruthy(); |
|
79 |
+ expect(uut.matches_specializer(1, String)).toBeFalsy(); |
|
80 |
+ }); |
|
81 |
+ |
|
82 |
+ test("handles strings", () => { |
|
83 |
+ expect("asdf").not.toBe(Object("asdf")); |
|
84 |
+ |
|
85 |
+ expect( |
|
86 |
+ uut.matches_specializer(new String("foobar"), String) |
|
87 |
+ ).toBeTruthy(); |
|
88 |
+ expect( |
|
89 |
+ uut.matches_specializer(new String("foobar"), Object) |
|
90 |
+ ).toBeTruthy(); |
|
91 |
+ |
|
92 |
+ expect(uut.matches_specializer("1", String)).toBeTruthy(); |
|
93 |
+ expect(uut.matches_specializer("1", Object)).toBeTruthy(); |
|
94 |
+ expect(uut.matches_specializer("1", Number)).toBeFalsy(); |
|
95 |
+ }); |
|
96 |
+ |
|
97 |
+ test("handles symbols", () => { |
|
98 |
+ const symbolPrim = Symbol("primitive"); |
|
99 |
+ const boxedSymbol = Object(symbolPrim); |
|
100 |
+ expect(symbolPrim).not.toBe(boxedSymbol); |
|
101 |
+ |
|
102 |
+ expect(uut.matches_specializer(boxedSymbol, Symbol)).toBeTruthy(); |
|
103 |
+ expect(uut.matches_specializer(boxedSymbol, Object)).toBeTruthy(); |
|
104 |
+ |
|
105 |
+ expect(uut.matches_specializer(symbolPrim, Symbol)).toBeTruthy(); |
|
106 |
+ expect(uut.matches_specializer(symbolPrim, Object)).toBeTruthy(); |
|
107 |
+ }); |
|
108 |
+ |
|
109 |
+ test("handles BigInt", () => { |
|
110 |
+ expect(4n).not.toBe(Object(4n)); |
|
111 |
+ |
|
112 |
+ expect(uut.matches_specializer(Object(4n), BigInt)).toBeTruthy(); |
|
113 |
+ expect(uut.matches_specializer(Object(4n), Object)).toBeTruthy(); |
|
114 |
+ |
|
115 |
+ expect(uut.matches_specializer(4n, BigInt)).toBeTruthy(); |
|
116 |
+ expect(uut.matches_specializer(4n, Object)).toBeTruthy(); |
|
117 |
+ }); |
|
118 |
+ }); |
|
119 |
+ |
|
49 | 120 |
test("works with custom specializers", () => { |
50 | 121 |
const AEql = makeCustomSpecializer(); |
51 | 122 |
expect(uut.matches_specializer("foo", new AEql("foo"))).toBeTruthy(); |
52 | 123 |
}); |
53 |
- |
|
54 |
- test("null behavior", () => { |
|
55 |
- expect(uut.matches_specializer(null, null)).toBeTruthy(); |
|
56 |
- expect(uut.matches_specializer(null, Number)).toBeFalsy(); |
|
57 |
- expect(uut.matches_specializer(null, String)).toBeFalsy(); |
|
58 |
- expect(uut.matches_specializer(null, Object)).toBeFalsy(); |
|
59 |
- }); |
|
60 |
- |
|
61 |
- test("undefined (the value) behavior", () => { |
|
62 |
- expect(uut.matches_specializer(undefined, undefined)).toBeTruthy(); |
|
63 |
- expect(uut.matches_specializer(undefined, Number)).toBeFalsy(); |
|
64 |
- expect(uut.matches_specializer(undefined, String)).toBeFalsy(); |
|
65 |
- expect(uut.matches_specializer(undefined, Object)).toBeFalsy(); |
|
66 |
- }); |
|
67 |
- |
|
68 |
- test("works for numbers", () => { |
|
69 |
- expect(uut.matches_specializer(new Number(1), Number)).toBeTruthy(); |
|
70 |
- expect(uut.matches_specializer(new Number(1), Object)).toBeTruthy(); |
|
71 |
- expect(uut.matches_specializer(new Number(1), String)).toBeFalsy(); |
|
72 |
- |
|
73 |
- expect(uut.matches_specializer(1, Number)).toBeTruthy(); |
|
74 |
- expect(uut.matches_specializer(1, Object)).toBeTruthy(); |
|
75 |
- expect(uut.matches_specializer(1, String)).toBeFalsy(); |
|
76 |
- }); |
|
77 |
- |
|
78 |
- test("handles strings", () => { |
|
79 |
- expect(uut.matches_specializer(new String("foobar"), String)).toBeTruthy(); |
|
80 |
- expect(uut.matches_specializer(new String("foobar"), Object)).toBeTruthy(); |
|
81 |
- |
|
82 |
- expect(uut.matches_specializer("1", String)).toBeTruthy(); |
|
83 |
- expect(uut.matches_specializer("1", Object)).toBeTruthy(); |
|
84 |
- expect(uut.matches_specializer("1", Number)).toBeFalsy(); |
|
85 |
- }); |
|
86 | 124 |
}); |
87 | 125 |
|
88 | 126 |
describe("defgeneric", () => { |