UNPKG

5.04 kBJavaScriptView Raw
1/*!
2 * Copyright (c) 2017-2018 by The Funfix Project Developers.
3 * Some rights reserved.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17/**
18 * Test if the given reference is a value object.
19 *
20 * Value objects are objects that implement the [[IEquals]]
21 * interface.
22 *
23 * @param ref is the reference to test
24 */
25export function isValueObject(ref) {
26 return !!(ref &&
27 typeof ref.equals === "function" &&
28 typeof ref.hashCode === "function");
29}
30/**
31 * Tests for universal equality.
32 *
33 * First attempting a reference check with `===`,
34 * after which it tries to fallback on [[IEquals]], if the
35 * left-hand side is implementing it.
36 *
37 * ```typescript
38 * equals(10, 10) // true, because 10 === 10
39 *
40 * class Box implements IEquals<Box> {
41 * constructor(value: number) { this.value = value }
42 *
43 * equals(other) { return this.value === other.value }
44 * hashCode() { return this.value << 2 }
45 * }
46 *
47 * // false, because they are not the same reference
48 * new Box(10) === new Box(10)
49 *
50 * // true, because `Box#equals` gets called
51 * equals(new Box(10), new Box(10))
52 * ```
53 */
54export function is(lh, rh) {
55 if (lh === rh || (lh !== lh && rh !== rh)) {
56 return true;
57 }
58 if (!lh || !rh) {
59 return false;
60 }
61 /* istanbul ignore else */
62 /* tslint:disable-next-line:strict-type-predicates */
63 if (typeof lh.valueOf === "function" && typeof rh.valueOf === "function") {
64 const lh2 = lh.valueOf();
65 const rh2 = rh.valueOf();
66 if (lh2 === rh2 || (lh2 !== lh2 && rh2 !== rh2)) {
67 return true;
68 }
69 if (!lh2 || !rh2) {
70 return false;
71 }
72 }
73 // noinspection PointlessBooleanExpressionJS
74 return !!(isValueObject(lh) &&
75 lh.equals(rh));
76}
77/** Alias for [[is]]. */
78export function equals(lh, rh) {
79 return is(lh, rh);
80}
81/**
82 * Returns a `Setoid` type-class instance that depends
83 * universal equality, as defined by {@link is}.
84 */
85export const universalSetoid = { equals };
86/**
87 * Universal hash-code function.
88 *
89 * Depending on the given value, it calculates the hash-code like so:
90 *
91 * 1. if it's a `number`, then it gets truncated
92 * to an integer and returned
93 * 2. if it's a "value object" (see [[isValueObject]]), then
94 * its `hashCode` is used
95 * 3. if a `valueOf()` function is provided, then the
96 * `hashCode` gets recursively invoked on its result
97 * 4. if all else fails, the value gets coerced to a `String`
98 * and a hash code is calculated using [[hashCodeOfString]]
99 *
100 * @param ref is the value to use for calculating a hash code
101 * @return an integer with the aforementioned properties
102 */
103export function hashCode(ref) {
104 if (typeof ref === "number") {
105 return ref & ref;
106 }
107 /* istanbul ignore else */
108 if (typeof ref.valueOf === "function") {
109 const v = ref.valueOf();
110 if (v !== ref)
111 return hashCode(v);
112 }
113 if (isValueObject(ref)) {
114 return ref.hashCode();
115 }
116 return hashCodeOfString(String(ref));
117}
118/**
119 * Calculates a hash code out of any string.
120 */
121export function hashCodeOfString(str) {
122 let hash = 0;
123 /* tslint:disable-next-line:strict-type-predicates */
124 if (str == null || str.length === 0)
125 return hash;
126 for (let i = 0; i < str.length; i++) {
127 const character = str.charCodeAt(i);
128 hash = ((hash << 5) - hash) + character;
129 hash = hash & hash; // Convert to 32bit integer
130 }
131 return hash;
132}
133/** The identity function. */
134export function id(a) {
135 return a;
136}
137/**
138 * Utility function for implementing mixins, based on the
139 * [TypeScript Mixins]{@link https://www.typescriptlang.org/docs/handbook/mixins.html}
140 * documentation.
141 *
142 * Sample:
143 *
144 * ```typescript
145 * class Disposable { ... }
146 * class Activatable { ... }
147 * class SmartObject implements Disposable, Activatable { ... }
148 *
149 * applyMixins(SmartObject, [Disposable, Activatable]);
150 * ```
151 *
152 * Using `implements` instead of `extends` for base classes
153 * will make the type system treat them like interfaces instead of
154 * classes. And by `applyMixins` we can also supply global
155 * implementations for the non-abstract members.
156 */
157export function applyMixins(derivedCtor, baseCtors) {
158 baseCtors.forEach(baseCtor => {
159 Object.getOwnPropertyNames(baseCtor.prototype).forEach(name => {
160 if (!derivedCtor.prototype[name])
161 derivedCtor.prototype[name] = baseCtor.prototype[name];
162 });
163 });
164}
165//# sourceMappingURL=std.js.map
\No newline at end of file