ts-enum-util
Advanced tools
Comparing version 4.0.1 to 5.0.0-beta.0
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var createUnhandledEntryError_1 = require("./createUnhandledEntryError"); | ||
var errorUtil_1 = require("./errorUtil"); | ||
var symbols_1 = require("./symbols"); | ||
@@ -17,3 +17,3 @@ var EnumValueMappee = (function () { | ||
else { | ||
throw new Error("Unexpected value: " + this.value); | ||
throw errorUtil_1.createUnexpectedValueError(this.value); | ||
} | ||
@@ -35,3 +35,3 @@ }; | ||
else { | ||
throw new Error("Unexpected value: null"); | ||
throw errorUtil_1.createUnexpectedValueError(null); | ||
} | ||
@@ -53,3 +53,3 @@ }; | ||
else { | ||
throw new Error("Unexpected value: undefined"); | ||
throw errorUtil_1.createUnexpectedValueError(undefined); | ||
} | ||
@@ -62,3 +62,3 @@ }; | ||
if (entry === symbols_1.unhandledEntry) { | ||
throw createUnhandledEntryError_1.createUnhandledEntryError(value); | ||
throw errorUtil_1.createUnhandledEntryError(value); | ||
} | ||
@@ -65,0 +65,0 @@ else { |
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var symbols_1 = require("./symbols"); | ||
var createUnhandledEntryError_1 = require("./createUnhandledEntryError"); | ||
var errorUtil_1 = require("./errorUtil"); | ||
var EnumValueVisitee = (function () { | ||
@@ -15,7 +15,6 @@ function EnumValueVisitee(value) { | ||
else if (visitor[symbols_1.handleUnexpected]) { | ||
return processEntry(visitor[symbols_1.handleUnexpected], this | ||
.value); | ||
return processEntry(visitor[symbols_1.handleUnexpected], this.value); | ||
} | ||
else { | ||
throw new Error("Unexpected value: " + this.value); | ||
throw errorUtil_1.createUnexpectedValueError(this.value); | ||
} | ||
@@ -37,3 +36,3 @@ }; | ||
else { | ||
throw new Error("Unexpected value: null"); | ||
throw errorUtil_1.createUnexpectedValueError(null); | ||
} | ||
@@ -55,3 +54,3 @@ }; | ||
else { | ||
throw new Error("Unexpected value: undefined"); | ||
throw errorUtil_1.createUnexpectedValueError(undefined); | ||
} | ||
@@ -64,3 +63,3 @@ }; | ||
if (entry === symbols_1.unhandledEntry) { | ||
throw createUnhandledEntryError_1.createUnhandledEntryError(value); | ||
throw errorUtil_1.createUnhandledEntryError(value); | ||
} | ||
@@ -67,0 +66,0 @@ else { |
@@ -6,7 +6,27 @@ "use strict"; | ||
function EnumWrapper(enumObj) { | ||
this.enumObj = enumObj; | ||
var _this = this; | ||
this.values = Symbol.iterator in Array.prototype | ||
? function () { return _this.valuesList[Symbol.iterator](); } | ||
: function () { | ||
var _a; | ||
var index = 0; | ||
return _a = { | ||
next: function () { | ||
var isDone = index >= _this.length; | ||
var result = { | ||
done: isDone, | ||
value: _this.valuesList[index] | ||
}; | ||
++index; | ||
return result; | ||
} | ||
}, | ||
_a[Symbol.iterator] = function () { | ||
return this; | ||
}, | ||
_a; | ||
}; | ||
this.keysList = Object.freeze(objectKeysUtil_1.getOwnEnumerableNonNumericKeys(enumObj)); | ||
var length = this.keysList.length; | ||
var valuesList = new Array(length); | ||
var keysByValueMap = new Map(); | ||
for (var index = 0; index < length; ++index) { | ||
@@ -16,17 +36,8 @@ var key = this.keysList[index]; | ||
valuesList[index] = value; | ||
keysByValueMap.set(value, key); | ||
this[index] = Object.freeze([key, value]); | ||
} | ||
this.valuesList = Object.freeze(valuesList); | ||
this.keysByValueMap = keysByValueMap; | ||
this.size = this.length = length; | ||
Object.freeze(this); | ||
} | ||
Object.defineProperty(EnumWrapper.prototype, Symbol.toStringTag, { | ||
get: function () { | ||
return "EnumWrapper"; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
EnumWrapper.prototype.toString = function () { | ||
@@ -36,4 +47,4 @@ return "[object EnumWrapper]"; | ||
EnumWrapper.prototype.keys = function () { | ||
var _a; | ||
var _this = this; | ||
var _a; | ||
var index = 0; | ||
@@ -56,25 +67,5 @@ return _a = { | ||
}; | ||
EnumWrapper.prototype.values = function () { | ||
var _this = this; | ||
EnumWrapper.prototype.entries = function () { | ||
var _a; | ||
var index = 0; | ||
return _a = { | ||
next: function () { | ||
var isDone = index >= _this.length; | ||
var result = { | ||
done: isDone, | ||
value: _this.valuesList[index] | ||
}; | ||
++index; | ||
return result; | ||
} | ||
}, | ||
_a[Symbol.iterator] = function () { | ||
return this; | ||
}, | ||
_a; | ||
}; | ||
EnumWrapper.prototype.entries = function () { | ||
var _this = this; | ||
var _a; | ||
var index = 0; | ||
@@ -132,5 +123,3 @@ return _a = { | ||
EnumWrapper.prototype.isKey = function (key) { | ||
return (key != null && | ||
objectKeysUtil_1.isNonNumericKey(key) && | ||
this.enumObj.hasOwnProperty(key)); | ||
return this.keysList.indexOf(key) !== -1; | ||
}; | ||
@@ -146,2 +135,3 @@ EnumWrapper.prototype.asKeyOrThrow = function (key) { | ||
EnumWrapper.prototype.asKeyOrDefault = function (key, defaultKey) { | ||
var verifiedDefaultKey = defaultKey != null ? this.asKeyOrThrow(defaultKey) : undefined; | ||
if (this.isKey(key)) { | ||
@@ -151,7 +141,7 @@ return key; | ||
else { | ||
return defaultKey; | ||
return verifiedDefaultKey; | ||
} | ||
}; | ||
EnumWrapper.prototype.isValue = function (value) { | ||
return value != null && this.keysByValueMap.has(value); | ||
return this.valuesList.indexOf(value) !== -1; | ||
}; | ||
@@ -167,2 +157,5 @@ EnumWrapper.prototype.asValueOrThrow = function (value) { | ||
EnumWrapper.prototype.asValueOrDefault = function (value, defaultValue) { | ||
var verifiedDefaultValue = defaultValue != null | ||
? this.asValueOrThrow(defaultValue) | ||
: undefined; | ||
if (this.isValue(value)) { | ||
@@ -172,10 +165,14 @@ return value; | ||
else { | ||
return defaultValue; | ||
return verifiedDefaultValue; | ||
} | ||
}; | ||
EnumWrapper.prototype.getKeyOrThrow = function (value) { | ||
var result = value != null ? this.keysByValueMap.get(value) : undefined; | ||
if (result != null) { | ||
return result; | ||
EnumWrapper.prototype.getKey = function (value, defaultKey) { | ||
var verifiedDefaultKey = defaultKey != null ? this.asKeyOrThrow(defaultKey) : undefined; | ||
if (value == null) { | ||
return verifiedDefaultKey; | ||
} | ||
var index = this.valuesList.indexOf(value); | ||
if (index !== -1) { | ||
return this.keysList[index]; | ||
} | ||
else { | ||
@@ -185,20 +182,15 @@ throw new Error("Unexpected value: " + value + ". Expected one of: " + this.getValues()); | ||
}; | ||
EnumWrapper.prototype.getKeyOrDefault = function (value, defaultKey) { | ||
var result = value != null ? this.keysByValueMap.get(value) : undefined; | ||
if (result != null) { | ||
return result; | ||
EnumWrapper.prototype.getValue = function (key, defaultValue) { | ||
var verifiedDefaultValue = defaultValue != null | ||
? this.asValueOrThrow(defaultValue) | ||
: undefined; | ||
if (key == null) { | ||
return verifiedDefaultValue; | ||
} | ||
else { | ||
return defaultKey; | ||
var index = this.keysList.indexOf(key); | ||
if (index !== -1) { | ||
return this.valuesList[index]; | ||
} | ||
}; | ||
EnumWrapper.prototype.getValueOrThrow = function (key) { | ||
return this.enumObj[this.asKeyOrThrow(key)]; | ||
}; | ||
EnumWrapper.prototype.getValueOrDefault = function (key, defaultValue) { | ||
if (this.isKey(key)) { | ||
return this.enumObj[key]; | ||
} | ||
else { | ||
return defaultValue; | ||
throw new Error("Unexpected key: " + key + ". Expected one of: " + this.getKeys()); | ||
} | ||
@@ -209,2 +201,3 @@ }; | ||
exports.EnumWrapper = EnumWrapper; | ||
EnumWrapper.prototype[Symbol.toStringTag] = "EnumWrapper"; | ||
//# sourceMappingURL=EnumWrapper.js.map |
@@ -1,2 +0,2 @@ | ||
import { createUnhandledEntryError } from "./createUnhandledEntryError"; | ||
import { createUnhandledEntryError, createUnexpectedValueError } from "./errorUtil"; | ||
import { handleUnexpected, handleNull, handleUndefined, unhandledEntry } from "./symbols"; | ||
@@ -15,3 +15,3 @@ var EnumValueMappee = (function () { | ||
else { | ||
throw new Error("Unexpected value: " + this.value); | ||
throw createUnexpectedValueError(this.value); | ||
} | ||
@@ -33,3 +33,3 @@ }; | ||
else { | ||
throw new Error("Unexpected value: null"); | ||
throw createUnexpectedValueError(null); | ||
} | ||
@@ -51,3 +51,3 @@ }; | ||
else { | ||
throw new Error("Unexpected value: undefined"); | ||
throw createUnexpectedValueError(undefined); | ||
} | ||
@@ -54,0 +54,0 @@ }; |
import { handleUnexpected, handleNull, handleUndefined, unhandledEntry } from "./symbols"; | ||
import { createUnhandledEntryError } from "./createUnhandledEntryError"; | ||
import { createUnhandledEntryError, createUnexpectedValueError } from "./errorUtil"; | ||
var EnumValueVisitee = (function () { | ||
@@ -13,7 +13,6 @@ function EnumValueVisitee(value) { | ||
else if (visitor[handleUnexpected]) { | ||
return processEntry(visitor[handleUnexpected], this | ||
.value); | ||
return processEntry(visitor[handleUnexpected], this.value); | ||
} | ||
else { | ||
throw new Error("Unexpected value: " + this.value); | ||
throw createUnexpectedValueError(this.value); | ||
} | ||
@@ -35,3 +34,3 @@ }; | ||
else { | ||
throw new Error("Unexpected value: null"); | ||
throw createUnexpectedValueError(null); | ||
} | ||
@@ -53,3 +52,3 @@ }; | ||
else { | ||
throw new Error("Unexpected value: undefined"); | ||
throw createUnexpectedValueError(undefined); | ||
} | ||
@@ -56,0 +55,0 @@ }; |
@@ -1,9 +0,29 @@ | ||
import { isNonNumericKey, getOwnEnumerableNonNumericKeys } from "./objectKeysUtil"; | ||
import { getOwnEnumerableNonNumericKeys } from "./objectKeysUtil"; | ||
var EnumWrapper = (function () { | ||
function EnumWrapper(enumObj) { | ||
this.enumObj = enumObj; | ||
var _this = this; | ||
this.values = Symbol.iterator in Array.prototype | ||
? function () { return _this.valuesList[Symbol.iterator](); } | ||
: function () { | ||
var _a; | ||
var index = 0; | ||
return _a = { | ||
next: function () { | ||
var isDone = index >= _this.length; | ||
var result = { | ||
done: isDone, | ||
value: _this.valuesList[index] | ||
}; | ||
++index; | ||
return result; | ||
} | ||
}, | ||
_a[Symbol.iterator] = function () { | ||
return this; | ||
}, | ||
_a; | ||
}; | ||
this.keysList = Object.freeze(getOwnEnumerableNonNumericKeys(enumObj)); | ||
var length = this.keysList.length; | ||
var valuesList = new Array(length); | ||
var keysByValueMap = new Map(); | ||
for (var index = 0; index < length; ++index) { | ||
@@ -13,17 +33,8 @@ var key = this.keysList[index]; | ||
valuesList[index] = value; | ||
keysByValueMap.set(value, key); | ||
this[index] = Object.freeze([key, value]); | ||
} | ||
this.valuesList = Object.freeze(valuesList); | ||
this.keysByValueMap = keysByValueMap; | ||
this.size = this.length = length; | ||
Object.freeze(this); | ||
} | ||
Object.defineProperty(EnumWrapper.prototype, Symbol.toStringTag, { | ||
get: function () { | ||
return "EnumWrapper"; | ||
}, | ||
enumerable: true, | ||
configurable: true | ||
}); | ||
EnumWrapper.prototype.toString = function () { | ||
@@ -33,4 +44,4 @@ return "[object EnumWrapper]"; | ||
EnumWrapper.prototype.keys = function () { | ||
var _a; | ||
var _this = this; | ||
var _a; | ||
var index = 0; | ||
@@ -53,25 +64,5 @@ return _a = { | ||
}; | ||
EnumWrapper.prototype.values = function () { | ||
var _this = this; | ||
EnumWrapper.prototype.entries = function () { | ||
var _a; | ||
var index = 0; | ||
return _a = { | ||
next: function () { | ||
var isDone = index >= _this.length; | ||
var result = { | ||
done: isDone, | ||
value: _this.valuesList[index] | ||
}; | ||
++index; | ||
return result; | ||
} | ||
}, | ||
_a[Symbol.iterator] = function () { | ||
return this; | ||
}, | ||
_a; | ||
}; | ||
EnumWrapper.prototype.entries = function () { | ||
var _this = this; | ||
var _a; | ||
var index = 0; | ||
@@ -129,5 +120,3 @@ return _a = { | ||
EnumWrapper.prototype.isKey = function (key) { | ||
return (key != null && | ||
isNonNumericKey(key) && | ||
this.enumObj.hasOwnProperty(key)); | ||
return this.keysList.indexOf(key) !== -1; | ||
}; | ||
@@ -143,2 +132,3 @@ EnumWrapper.prototype.asKeyOrThrow = function (key) { | ||
EnumWrapper.prototype.asKeyOrDefault = function (key, defaultKey) { | ||
var verifiedDefaultKey = defaultKey != null ? this.asKeyOrThrow(defaultKey) : undefined; | ||
if (this.isKey(key)) { | ||
@@ -148,7 +138,7 @@ return key; | ||
else { | ||
return defaultKey; | ||
return verifiedDefaultKey; | ||
} | ||
}; | ||
EnumWrapper.prototype.isValue = function (value) { | ||
return value != null && this.keysByValueMap.has(value); | ||
return this.valuesList.indexOf(value) !== -1; | ||
}; | ||
@@ -164,2 +154,5 @@ EnumWrapper.prototype.asValueOrThrow = function (value) { | ||
EnumWrapper.prototype.asValueOrDefault = function (value, defaultValue) { | ||
var verifiedDefaultValue = defaultValue != null | ||
? this.asValueOrThrow(defaultValue) | ||
: undefined; | ||
if (this.isValue(value)) { | ||
@@ -169,10 +162,14 @@ return value; | ||
else { | ||
return defaultValue; | ||
return verifiedDefaultValue; | ||
} | ||
}; | ||
EnumWrapper.prototype.getKeyOrThrow = function (value) { | ||
var result = value != null ? this.keysByValueMap.get(value) : undefined; | ||
if (result != null) { | ||
return result; | ||
EnumWrapper.prototype.getKey = function (value, defaultKey) { | ||
var verifiedDefaultKey = defaultKey != null ? this.asKeyOrThrow(defaultKey) : undefined; | ||
if (value == null) { | ||
return verifiedDefaultKey; | ||
} | ||
var index = this.valuesList.indexOf(value); | ||
if (index !== -1) { | ||
return this.keysList[index]; | ||
} | ||
else { | ||
@@ -182,20 +179,15 @@ throw new Error("Unexpected value: " + value + ". Expected one of: " + this.getValues()); | ||
}; | ||
EnumWrapper.prototype.getKeyOrDefault = function (value, defaultKey) { | ||
var result = value != null ? this.keysByValueMap.get(value) : undefined; | ||
if (result != null) { | ||
return result; | ||
EnumWrapper.prototype.getValue = function (key, defaultValue) { | ||
var verifiedDefaultValue = defaultValue != null | ||
? this.asValueOrThrow(defaultValue) | ||
: undefined; | ||
if (key == null) { | ||
return verifiedDefaultValue; | ||
} | ||
else { | ||
return defaultKey; | ||
var index = this.keysList.indexOf(key); | ||
if (index !== -1) { | ||
return this.valuesList[index]; | ||
} | ||
}; | ||
EnumWrapper.prototype.getValueOrThrow = function (key) { | ||
return this.enumObj[this.asKeyOrThrow(key)]; | ||
}; | ||
EnumWrapper.prototype.getValueOrDefault = function (key, defaultValue) { | ||
if (this.isKey(key)) { | ||
return this.enumObj[key]; | ||
} | ||
else { | ||
return defaultValue; | ||
throw new Error("Unexpected key: " + key + ". Expected one of: " + this.getKeys()); | ||
} | ||
@@ -206,2 +198,3 @@ }; | ||
export { EnumWrapper }; | ||
EnumWrapper.prototype[Symbol.toStringTag] = "EnumWrapper"; | ||
//# sourceMappingURL=EnumWrapper.js.map |
import { EnumWrapper } from "./EnumWrapper"; | ||
import { StringKeyOf } from "./types"; | ||
import { EnumObject } from "./EnumObject"; | ||
import * as symbols from "./symbols"; | ||
@@ -7,3 +7,3 @@ import { visitEnumValue } from "./visitEnumValue"; | ||
/** | ||
* Gets a cached EnumWrapper for an enum-like object with number values. | ||
* Gets a cached EnumWrapper for an enum-like object. | ||
* Creates and caches a new EnumWrapper if one is not already cached. | ||
@@ -13,5 +13,5 @@ * @param enumObj - An enum-like object with number values. | ||
* | ||
* @template T - Type of the enum-like object that is being wrapped. | ||
* @template E - Type of the enum-like object that is being wrapped. | ||
*/ | ||
export declare function $enum<V extends number, T extends Record<StringKeyOf<T>, number>>(enumObj: T): EnumWrapper<number, T>; | ||
export declare function $enum<E extends EnumObject.Constraint<E>>(enumObj: E): EnumWrapper<E>; | ||
export declare namespace $enum { | ||
@@ -25,38 +25,2 @@ var handleNull: typeof symbols.handleNull; | ||
} | ||
/** | ||
* Gets a cached EnumWrapper for an enum-like object with string values. | ||
* Creates and caches a new EnumWrapper if one is not already cached. | ||
* @param enumObj - An enum-like object with string values. | ||
* @return An instance of EnumWrapper for the provided enumObj. | ||
* | ||
* @template T - Type of the enum-like object that is being wrapped. | ||
*/ | ||
export declare function $enum<T extends Record<StringKeyOf<T>, string>>(enumObj: T): EnumWrapper<string, T>; | ||
export declare namespace $enum { | ||
var handleNull: typeof symbols.handleNull; | ||
var handleUndefined: typeof symbols.handleUndefined; | ||
var handleUnexpected: typeof symbols.handleUnexpected; | ||
var unhandledEntry: typeof symbols.unhandledEntry; | ||
var visitValue: typeof visitEnumValue; | ||
var mapValue: typeof mapEnumValue; | ||
} | ||
/** | ||
* Gets a cached EnumWrapper for an enum-like object with a mixture of number | ||
* and string values. | ||
* Creates and caches a new EnumWrapper if one is not already cached. | ||
* @param enumObj - An enum-like object with a mixture of number and string | ||
* values. | ||
* @return An instance of EnumWrapper for the provided enumObj. | ||
* | ||
* @template T - Type of the enum-like object that is being wrapped. | ||
*/ | ||
export declare function $enum<T extends Record<StringKeyOf<T>, number | string>>(enumObj: T): EnumWrapper<number | string, T>; | ||
export declare namespace $enum { | ||
var handleNull: typeof symbols.handleNull; | ||
var handleUndefined: typeof symbols.handleUndefined; | ||
var handleUnexpected: typeof symbols.handleUnexpected; | ||
var unhandledEntry: typeof symbols.unhandledEntry; | ||
var visitValue: typeof visitEnumValue; | ||
var mapValue: typeof mapEnumValue; | ||
} | ||
//# sourceMappingURL=$enum.d.ts.map |
import { EnumValueVisitor, EnumValueVisitorWithNull, EnumValueVisitorWithUndefined, EnumValueVisitorWithNullAndUndefined } from "./EnumValueVisitor"; | ||
/** | ||
* A wrapper around a string literal or string enum value to be visited. | ||
* A wrapper around an enum or string/number literal value to be visited. | ||
* Do not use this class directly. Use the {@link visitString} function to get an instance of this class. | ||
* | ||
* @template E - A string literal type or string enum type. | ||
* @template E - An enum type or string/number literal union type. | ||
*/ | ||
@@ -27,3 +27,3 @@ export declare class EnumValueVisitee<E extends string | number> { | ||
/** | ||
* A wrapper around a string literal or string enum value to be visited. | ||
* A wrapper around an enum or string/number literal value to be visited. | ||
* For values that may be null. | ||
@@ -36,3 +36,3 @@ * Do not use this class directly. Use the {@link visitString} function to get an instance of this class. | ||
* | ||
* @template E - A string literal type or string enum type. | ||
* @template E - An enum type or string/number literal union type. | ||
*/ | ||
@@ -53,3 +53,3 @@ export declare class EnumValueVisiteeWithNull<E extends string | number> { | ||
/** | ||
* A wrapper around a string literal or string enum value to be visited. | ||
* A wrapper around an enum or string/number literal value to be visited. | ||
* For values that may be undefined. | ||
@@ -62,3 +62,3 @@ * Do not use this class directly. Use the {@link visitString} function to get an instance of this class. | ||
* | ||
* @template E - A string literal type or string enum type. | ||
* @template E - An enum type or string/number literal union type. | ||
*/ | ||
@@ -79,3 +79,3 @@ export declare class EnumValueVisiteeWithUndefined<E extends string | number> { | ||
/** | ||
* A wrapper around a string literal or string enum value to be visited. | ||
* A wrapper around an enum or string/number literal value to be visited. | ||
* For values that may be null and undefined. | ||
@@ -89,3 +89,3 @@ * Do not use this class directly. Use the {@link visitString} function to get an instance of this class. | ||
* | ||
* @template E - A string literal type or string enum type. | ||
* @template E - An enum type or string/number literal union type. | ||
*/ | ||
@@ -92,0 +92,0 @@ export declare class EnumValueVisiteeWithNullAndUndefined<E extends string | number> { |
import { handleUnexpected, handleNull, handleUndefined, unhandledEntry } from "./symbols"; | ||
/** | ||
* Helper type to widen a number/string enum/literal type to plain string or number. | ||
*/ | ||
export declare type WidenEnumType<E extends number | string> = (E extends number ? number : never) | (E extends string ? string : never); | ||
/** | ||
* Generic method signature for a string visitor handler method. | ||
* @template E - The type of the parameter to the handler. Must be a string literal, null, or undefined. | ||
* Generic method signature for a value visitor handler method. | ||
* @template T - The type of the value. | ||
* @template R - The return type of the handler. Defaults to void. | ||
@@ -13,8 +9,8 @@ * @param value - The value being visited by the visitor. | ||
*/ | ||
export declare type EnumValueVisitorHandler<E extends string | number | null | undefined, R = void> = (value: E) => R; | ||
export declare type EnumValueVisitorHandler<T, R = void> = (value: T) => R; | ||
/** | ||
* Core definition of all string visitor interfaces. | ||
* Core definition of all value visitor interfaces. | ||
* Defines visitor handler properties for each possible value of type `E`. | ||
* | ||
* @template E - A string literal type or string enum type. | ||
* @template E - An enum type or string/number literal union type. | ||
* @template R - The return type of the visitor methods. | ||
@@ -26,2 +22,11 @@ */ | ||
/** | ||
* A visitor interface for visiting an unexpected value. | ||
* This is never used by itself, but combined with {@link EnumValueVisitor} as needed. | ||
* | ||
* @template R - The return type of the visitor method. | ||
*/ | ||
export interface UnexpectedEnumValueVisitor<R> { | ||
[handleUnexpected]?: EnumValueVisitorHandler<any, R> | typeof unhandledEntry; | ||
} | ||
/** | ||
* A visitor interface for visiting a null value. | ||
@@ -45,40 +50,33 @@ * This is never used by itself, but combined with {@link EnumValueVisitor} as needed. | ||
/** | ||
* A visitor interface for visiting the value of a string literal type or a string enum type. | ||
* A visitor interface for visiting the value of an enum type or string/number literal union type. | ||
* | ||
* @template E - A string literal type or string enum type. | ||
* @template E - An enum type or string/number literal union type. | ||
* @template R - The return type of the visitor methods. | ||
*/ | ||
export declare type EnumValueVisitor<E extends string | number, R> = EnumValueVisitorCore<E, R> & { | ||
[handleUnexpected]?: EnumValueVisitorHandler<WidenEnumType<E> | null | undefined, R> | typeof unhandledEntry; | ||
}; | ||
export declare type EnumValueVisitor<E extends string | number, R> = EnumValueVisitorCore<E, R> & UnexpectedEnumValueVisitor<R>; | ||
/** | ||
* Combines {@link EnumValueVisitor} with {@link NullEnumValueVisitor} for visiting a string literal/enum | ||
* that may be null. | ||
* Combines {@link EnumValueVisitor} with {@link NullEnumValueVisitor} for | ||
* visiting an enum or string/number literal value that may be null. | ||
* | ||
* @template E - A string literal type or string enum type. | ||
* @template E - An enum type or string/number literal union type. | ||
* @template R - The return type of the visitor methods. | ||
*/ | ||
export declare type EnumValueVisitorWithNull<E extends string | number, R> = EnumValueVisitorCore<E, R> & NullEnumValueVisitor<R> & { | ||
[handleUnexpected]?: EnumValueVisitorHandler<WidenEnumType<E> | undefined, R> | typeof unhandledEntry; | ||
}; | ||
export declare type EnumValueVisitorWithNull<E extends string | number, R> = EnumValueVisitorCore<E, R> & NullEnumValueVisitor<R> & UnexpectedEnumValueVisitor<R>; | ||
/** | ||
* Combines {@link EnumValueVisitor} with {@link UndefinedEnumValueVisitor} for visiting a string literal/enum | ||
* that may be undefined. | ||
* Combines {@link EnumValueVisitor} with {@link UndefinedEnumValueVisitor} for | ||
* visiting an enum or string/number literal value that may be undefined. | ||
* | ||
* @template E - A string literal type or string enum type. | ||
* @template E - An enum type or string/number literal union type. | ||
* @template R - The return type of the visitor methods. | ||
*/ | ||
export declare type EnumValueVisitorWithUndefined<E extends string | number, R> = EnumValueVisitorCore<E, R> & UndefinedEnumValueVisitor<R> & { | ||
[handleUnexpected]?: EnumValueVisitorHandler<WidenEnumType<E> | null, R> | typeof unhandledEntry; | ||
}; | ||
export declare type EnumValueVisitorWithUndefined<E extends string | number, R> = EnumValueVisitorCore<E, R> & UndefinedEnumValueVisitor<R> & UnexpectedEnumValueVisitor<R>; | ||
/** | ||
* Combines {@link EnumValueVisitor} with {@link NullEnumValueVisitor} and {@link UndefinedEnumValueVisitor} | ||
* for visiting a string literal/enum that may be null or undefined. | ||
* Combines {@link EnumValueVisitor} with {@link NullEnumValueVisitor} and | ||
* {@link UndefinedEnumValueVisitor} for visiting an enum or string/number literal value | ||
* that may be null or undefined. | ||
* | ||
* @template E - A string literal type or string enum type. | ||
* @template E - An enum type or string/number literal union type. | ||
* @template R - The return type of the visitor methods. | ||
*/ | ||
export declare type EnumValueVisitorWithNullAndUndefined<E extends string | number, R> = EnumValueVisitorCore<E, R> & NullEnumValueVisitor<R> & UndefinedEnumValueVisitor<R> & { | ||
[handleUnexpected]?: EnumValueVisitorHandler<WidenEnumType<E>, R> | typeof unhandledEntry; | ||
}; | ||
export declare type EnumValueVisitorWithNullAndUndefined<E extends string | number, R> = EnumValueVisitorCore<E, R> & NullEnumValueVisitor<R> & UndefinedEnumValueVisitor<R> & UnexpectedEnumValueVisitor<R>; | ||
//# sourceMappingURL=EnumValueVisitor.d.ts.map |
@@ -1,3 +0,48 @@ | ||
import { StringKeyOf } from "./types"; | ||
import { StringKeyOf, StringKeyOfType } from "./types"; | ||
/** | ||
* Use StrictEnumParam to define the type of a function | ||
* parameter that should be strictly assignable to a numeric enum | ||
* type. This prevents arbitrary numbers from being passed in to | ||
* the parameter, working around TypeScript's intentional decision | ||
* to allow type `number` to be assignable to all numeric enum types. | ||
* | ||
* Instead of writing a function signature as: | ||
* function doSomething(value: MyEnum): void; | ||
* | ||
* Write it like this: | ||
* function doSomething<Value extends MyEnum>( | ||
* value: StrictEnumParam<MyEnum, Value> | ||
* ): void; | ||
* | ||
* StrictEnumParam<MyEnum, Value> will evaluate to `never` | ||
* for any type `Value` that is not strictly assignable to `MyEnum` | ||
* (e.g., type `number`, or any number literal type that is not one | ||
* of the valid values for `MyEnum`), and will produce a compiler | ||
* error such as: | ||
* "Argument of type `number` is not assignable to parameter of type `never`" | ||
* | ||
* LIMITATION: | ||
* This only works for a special subset of numeric enums that are considered | ||
* "Union Enums". For an enum to be compatible, it basically must be a simple | ||
* numeric enum where every member has either an inferred value | ||
* (previous enum member + 1), or a number literal (1, 42, -3, etc.). | ||
* | ||
* If the `Enum` type argument is not a "Union Enum", then this type resolves | ||
* to simply type `Enum` and the use of StrictEnumParam is neither | ||
* beneficial nor detrimental. | ||
*/ | ||
export declare type StrictEnumParam<Enum extends number | string, Param extends Enum> = true extends ({ | ||
[key: number]: false; | ||
} & { | ||
[P in Enum]: true; | ||
})[Extract<Enum, number>] ? true extends ({ | ||
[key: number]: false; | ||
} & { | ||
[P in Enum]: true; | ||
})[Param] ? Param : never : Param; | ||
/** | ||
* Widens types that are assignable to number/string to the full number/string type. | ||
*/ | ||
declare type Widen<T extends number | string> = T extends number ? number : T extends string ? string : T; | ||
/** | ||
* A generic wrapper for any enum-like object. | ||
@@ -7,10 +52,12 @@ * Provides utilities for runtime processing of an enum's values and keys, with strict compile-time | ||
* | ||
* EnumWrapper cannot be directly instantiated. Use {@link $enum} to get/create an EnumWrapper | ||
* instance. | ||
* EnumWrapper should generally not be directly instantiated. | ||
* Use {@link $enum} to get/create an EnumWrapper instance. | ||
* | ||
* @template V - Type of the enum value. | ||
* @template T - Type of the enum-like object that is being wrapped. | ||
* @template E - The type of the enum-like object that is wrapped by the EnumWrapper.. | ||
* @template V - The enum value type (always allow this to default!). | ||
* NOTE: This template param shouldn't need to exist. It is a workaround | ||
* to a TypeScript design limitation. | ||
* See: https://github.com/microsoft/TypeScript/issues/35322#issuecomment-558247434 | ||
*/ | ||
export declare class EnumWrapper<V extends number | string = number | string, T extends Record<StringKeyOf<T>, V> = any> implements Iterable<EnumWrapper.Entry<T>>, ArrayLike<EnumWrapper.Entry<T>> { | ||
private readonly enumObj; | ||
export declare class EnumWrapper<E extends Record<StringKeyOf<E>, V>, V extends string | number = E[StringKeyOf<E>]> implements Iterable<EnumWrapper.Entry<E, V>>, ArrayLike<EnumWrapper.Entry<E, V>> { | ||
/** | ||
@@ -25,10 +72,2 @@ * List of all keys for this enum, in the original defined order of the enum. | ||
/** | ||
* Map of enum value -> enum key. | ||
* Used for reverse key lookups. | ||
* NOTE: Performance tests show that using a Map (even if it's a slow polyfill) is faster than building a lookup | ||
* string key for values and using a plain Object: | ||
* {@link https://www.measurethat.net/Benchmarks/Show/2514/1/map-keyed-by-string-or-number} | ||
*/ | ||
private readonly keysByValueMap; | ||
/** | ||
* The number of entries in this enum. | ||
@@ -47,3 +86,3 @@ * Part of the Map-like interface. | ||
*/ | ||
readonly [key: number]: EnumWrapper.Entry<T>; | ||
readonly [key: number]: EnumWrapper.Entry<E, V>; | ||
/** | ||
@@ -56,4 +95,3 @@ * Create a new EnumWrapper instance. | ||
*/ | ||
constructor(enumObj: T); | ||
readonly [Symbol.toStringTag]: string; | ||
constructor(enumObj: E); | ||
/** | ||
@@ -69,3 +107,3 @@ * @return "[object EnumWrapper]" | ||
*/ | ||
keys(): IterableIterator<StringKeyOf<T>>; | ||
keys(): IterableIterator<StringKeyOf<E>>; | ||
/** | ||
@@ -79,3 +117,3 @@ * Get an iterator for this enum's values. | ||
*/ | ||
values(): IterableIterator<T[StringKeyOf<T>]>; | ||
readonly values: () => IterableIterator<V>; | ||
/** | ||
@@ -86,3 +124,3 @@ * Get an iterator for this enum's entries as [key, value] tuples. | ||
*/ | ||
entries(): IterableIterator<EnumWrapper.Entry<T>>; | ||
entries(): IterableIterator<EnumWrapper.Entry<E, V>>; | ||
/** | ||
@@ -93,3 +131,3 @@ * Get an iterator for this enum's entries as [key, value] tuples. | ||
*/ | ||
[Symbol.iterator](): IterableIterator<EnumWrapper.Entry<T>>; | ||
[Symbol.iterator](): IterableIterator<EnumWrapper.Entry<E, V>>; | ||
/** | ||
@@ -103,3 +141,3 @@ * Calls the provided iteratee on each item in this enum. | ||
*/ | ||
forEach(iteratee: EnumWrapper.Iteratee<void, V, T>, context?: any): void; | ||
forEach(iteratee: EnumWrapper.Iteratee<void, E, V>, context?: any): void; | ||
/** | ||
@@ -112,7 +150,7 @@ * Maps this enum's entries to a new list of values. | ||
* @param context - If provided, then the iteratee will be called with the context as its "this" value. | ||
* @return A new array containg the results of the iteratee. | ||
* @return A new array containing the results of the iteratee. | ||
* | ||
* @template R - The of the mapped result for each entry. | ||
*/ | ||
map<R>(iteratee: EnumWrapper.Iteratee<R, V, T>, context?: any): R[]; | ||
map<R>(iteratee: EnumWrapper.Iteratee<R, E, V>, context?: any): R[]; | ||
/** | ||
@@ -123,3 +161,3 @@ * Get a list of this enum's keys. | ||
*/ | ||
getKeys(): (StringKeyOf<T>)[]; | ||
getKeys(): StringKeyOf<E>[]; | ||
/** | ||
@@ -132,3 +170,3 @@ * Get a list of this enum's values. | ||
*/ | ||
getValues(): T[StringKeyOf<T>][]; | ||
getValues(): V[]; | ||
/** | ||
@@ -139,3 +177,3 @@ * Get a list of this enum's entries as [key, value] tuples. | ||
*/ | ||
getEntries(): EnumWrapper.Entry<T>[]; | ||
getEntries(): EnumWrapper.Entry<E, V>[]; | ||
/** | ||
@@ -146,3 +184,3 @@ * Get the index of a key based on the original defined order of this enum. | ||
*/ | ||
indexOfKey(key: StringKeyOf<T>): number; | ||
indexOfKey(key: StringKeyOf<E>): number; | ||
/** | ||
@@ -153,3 +191,3 @@ * Get the index of a value based on the original defined order of this enum. | ||
*/ | ||
indexOfValue(value: T[StringKeyOf<T>]): number; | ||
indexOfValue<Value extends V>(value: StrictEnumParam<V, Value>): number; | ||
/** | ||
@@ -161,3 +199,3 @@ * Tests if the provided string is actually a valid key for this enum | ||
*/ | ||
isKey(key: string | null | undefined): key is StringKeyOf<T>; | ||
isKey(key: string | null | undefined): key is StringKeyOf<E>; | ||
/** | ||
@@ -170,3 +208,3 @@ * Casts a string to a properly-typed key for this enum. | ||
*/ | ||
asKeyOrThrow(key: string | null | undefined): StringKeyOf<T>; | ||
asKeyOrThrow(key: string | null | undefined): StringKeyOf<E>; | ||
/** | ||
@@ -179,4 +217,5 @@ * Casts a string to a properly-typed key for this enum. | ||
* Returns `defaultKey` if the provided key is invalid. | ||
* @throws {Error} if `defaultKey` is not a valid key for this enum. | ||
*/ | ||
asKeyOrDefault(key: string | null | undefined, defaultKey: StringKeyOf<T>): StringKeyOf<T>; | ||
asKeyOrDefault(key: string | null | undefined, defaultKey: StringKeyOf<E>): StringKeyOf<E>; | ||
/** | ||
@@ -189,23 +228,6 @@ * Casts a string to a properly-typed key for this enum. | ||
* Returns `defaultKey` if the provided key is invalid. | ||
* @throws {Error} if `defaultKey` is not a valid key for this enum. | ||
*/ | ||
asKeyOrDefault(key: string | null | undefined, defaultKey?: StringKeyOf<T>): StringKeyOf<T> | undefined; | ||
asKeyOrDefault(key: string | null | undefined, defaultKey?: StringKeyOf<E>): StringKeyOf<E> | undefined; | ||
/** | ||
* Casts a string to a properly-typed key for this enum. | ||
* Returns a default key if the provided key is invalid. | ||
* @param key - A potential key value for this enum. | ||
* @param defaultKey - The key to be returned if the provided key is invalid. | ||
* @return The provided key value, cast to the type of this enum's keys. | ||
* Returns `defaultKey` if the provided key is invalid. | ||
*/ | ||
asKeyOrDefault(key: string | null | undefined, defaultKey: string): string; | ||
/** | ||
* Casts a string to a properly-typed key for this enum. | ||
* Returns a default key if the provided key is invalid. | ||
* @param key - A potential key value for this enum. | ||
* @param defaultKey - The key to be returned if the provided key is invalid. | ||
* @return The provided key value, cast to the type of this enum's keys. | ||
* Returns `defaultKey` if the provided key is invalid. | ||
*/ | ||
asKeyOrDefault(key: string | null | undefined, defaultKey: string | undefined): string | undefined; | ||
/** | ||
* Tests if the provided value is a valid value for this enum. | ||
@@ -216,3 +238,3 @@ * Acts as a type guard to confirm that the provided value is actually the enum value type. | ||
*/ | ||
isValue(value: V | null | undefined): value is T[StringKeyOf<T>]; | ||
isValue(value: Widen<V> | V | null | undefined): value is Widen<V> & V; | ||
/** | ||
@@ -225,3 +247,3 @@ * Casts a value to a properly-typed value for this enum. | ||
*/ | ||
asValueOrThrow(value: V | null | undefined): T[StringKeyOf<T>]; | ||
asValueOrThrow(value: Widen<V> | V | null | undefined): V; | ||
/** | ||
@@ -234,4 +256,5 @@ * Casts a value to a properly-typed value for this enum. | ||
* Returns `defaultValue` if the provided value is invalid. | ||
* @throws {Error} if `defaultValue` is not a valid value for this enum. | ||
*/ | ||
asValueOrDefault(value: V | null | undefined, defaultValue: T[StringKeyOf<T>]): T[StringKeyOf<T>]; | ||
asValueOrDefault<DefaultValue extends V>(value: Widen<V> | V | null | undefined, defaultValue: StrictEnumParam<V, DefaultValue>): V; | ||
/** | ||
@@ -244,120 +267,132 @@ * Casts a value to a properly-typed value for this enum. | ||
* Returns `defaultValue` if the provided value is invalid. | ||
* @throws {Error} if `defaultValue` is not a valid value for this enum. | ||
*/ | ||
asValueOrDefault(value: V | null | undefined, defaultValue?: T[StringKeyOf<T>]): T[StringKeyOf<T>] | undefined; | ||
asValueOrDefault<DefaultValue extends V>(value: Widen<V> | V | null | undefined, defaultValue?: StrictEnumParam<V, DefaultValue>): V | undefined; | ||
/** | ||
* Casts a value to a properly-typed value for this enum. | ||
* Returns a default value if the provided value is invalid. | ||
* @param value - A potential value for this enum. | ||
* @param defaultValue - The value to be returned if the provided value is invalid. | ||
* @return The provided value, cast to the type of this enum's values. | ||
* Returns `defaultValue` if the provided value is invalid. | ||
*/ | ||
asValueOrDefault(value: V | null | undefined, defaultValue: V): V; | ||
/** | ||
* Casts a value to a properly-typed value for this enum. | ||
* Returns a default value if the provided value is invalid. | ||
* @param value - A potential value for this enum. | ||
* @param defaultValue - The value to be returned if the provided value is invalid. | ||
* @return The provided value, cast to the type of this enum's values. | ||
* Returns `defaultValue` if the provided value is invalid. | ||
*/ | ||
asValueOrDefault(value: V | null | undefined, defaultValue: V | undefined): V | undefined; | ||
/** | ||
* Performs a reverse lookup from enum value to corresponding enum key. | ||
* Throws an error if the value is invalid. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the duplicated value is | ||
* arbitrarily returned. | ||
* @param value - A potential value for this enum. | ||
* Performs a strict reverse lookup from enum value to corresponding enum key. | ||
* This method is as strict as possible with compile-time types and run-time | ||
* validation for a lookup based on a (expected to be) valid value that is | ||
* guaranteed to return a valid key. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the | ||
* duplicated value is arbitrarily returned. | ||
* @param value - A valid value for this enum. | ||
* @return The key for the provided value. | ||
* @throws {Error} if the provided value is not a valid value for this enum. | ||
* @throws {Error} if the provided value is not valid for this enum. | ||
*/ | ||
getKeyOrThrow(value: V | null | undefined): StringKeyOf<T>; | ||
getKey<Value extends V>(value: Value & StrictEnumParam<V, Value>): StringKeyOfType<E, Value>; | ||
/** | ||
* Performs a reverse lookup from enum value to corresponding enum key. | ||
* Returns a default key if the provided value is invalid. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the duplicated value is | ||
* arbitrarily returned. | ||
* @param value - A potential value for this enum. | ||
* @param defaultKey - The key to be returned if the provided value is invalid. | ||
* Performs a strict reverse lookup from enum value to corresponding enum key. | ||
* This method is as strict as possible with compile-time types and run-time | ||
* validation for a lookup based on a (expected to be) valid value that is | ||
* guaranteed to return a valid key. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the | ||
* duplicated value is arbitrarily returned. | ||
* @param value - A valid value for this enum. | ||
* @return The key for the provided value. | ||
* Returns `defaultKey` if the provided value is invalid. | ||
* @throws {Error} if the provided value is not valid for this enum. | ||
*/ | ||
getKeyOrDefault(value: V | null | undefined, defaultKey: StringKeyOf<T>): StringKeyOf<T>; | ||
getKey<Value extends V>(value: (Value & StrictEnumParam<V, Value>) | null | undefined): StringKeyOfType<E, Value> | undefined; | ||
/** | ||
* Performs a reverse lookup from enum value to corresponding enum key. | ||
* Returns a default key if the provided value is invalid. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the duplicated value is | ||
* arbitrarily returned. | ||
* @param value - A potential value for this enum. | ||
* @param defaultKey - The key to be returned if the provided value is invalid. | ||
* @return The key for the provided value. | ||
* Returns `defaultKey` if the provided value is invalid. | ||
* Performs a strict reverse lookup from enum value to corresponding enum key, | ||
* with a default key to be returned if the provided value is null/undefined. | ||
* This method is as strict as possible with compile-time types and run-time | ||
* validation for a lookup based on a (expected to be) valid value that is | ||
* guaranteed to return a valid key. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the | ||
* duplicated value is arbitrarily returned. | ||
* @param value - A valid value for this enum (or null/undefined). | ||
* @param defaultKey - A valid key to be returned if the provided value is null/undefined. | ||
* @return The key for the provided value, or the default key. | ||
* @throws {Error} if the provided value or default key is not valid for this enum. | ||
*/ | ||
getKeyOrDefault(value: V | null | undefined, defaultKey?: StringKeyOf<T>): StringKeyOf<T> | undefined; | ||
getKey<Value extends V>(value: (Value & StrictEnumParam<V, Value>) | null | undefined, defaultKey: undefined): StringKeyOfType<E, Value> | undefined; | ||
/** | ||
* Performs a reverse lookup from enum value to corresponding enum key. | ||
* Returns a default key if the provided value is invalid. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the duplicated value is | ||
* arbitrarily returned. | ||
* @param value - A potential value for this enum. | ||
* @param defaultKey - The key to be returned if the provided value is invalid. | ||
* @return The key for the provided value. | ||
* Returns `defaultKey` if the provided value is invalid. | ||
* Performs a strict reverse lookup from enum value to corresponding enum key, | ||
* with a default key to be returned if the provided value is null/undefined. | ||
* This method is as strict as possible with compile-time types and run-time | ||
* validation for a lookup based on a (expected to be) valid value that is | ||
* guaranteed to return a valid key. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the | ||
* duplicated value is arbitrarily returned. | ||
* @param value - A valid value for this enum (or null/undefined). | ||
* @param defaultKey - A valid key to be returned if the provided value is null/undefined. | ||
* @return The key for the provided value, or the default key. | ||
* @throws {Error} if the provided value or default key is not valid for this enum. | ||
*/ | ||
getKeyOrDefault(value: V | null | undefined, defaultKey: string): string; | ||
getKey<Value extends V, DefaultKey extends StringKeyOf<E>>(value: (Value & StrictEnumParam<V, Value>) | null | undefined, defaultKey: DefaultKey): StringKeyOfType<E, Value> | DefaultKey; | ||
/** | ||
* Performs a reverse lookup from enum value to corresponding enum key. | ||
* Returns a default key if the provided value is invalid. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the duplicated value is | ||
* arbitrarily returned. | ||
* @param value - A potential value for this enum. | ||
* @param defaultKey - The key to be returned if the provided value is invalid. | ||
* @return The key for the provided value. | ||
* Returns `defaultKey` if the provided value is invalid. | ||
* Performs a strict reverse lookup from enum value to corresponding enum key, | ||
* with a default key to be returned if the provided value is null/undefined. | ||
* This method is as strict as possible with compile-time types and run-time | ||
* validation for a lookup based on a (expected to be) valid value that is | ||
* guaranteed to return a valid key. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the | ||
* duplicated value is arbitrarily returned. | ||
* @param value - A valid value for this enum (or null/undefined). | ||
* @param defaultKey - A valid key to be returned if the provided value is null/undefined. | ||
* @return The key for the provided value, or the default key. | ||
* @throws {Error} if the provided value or default key is not valid for this enum. | ||
*/ | ||
getKeyOrDefault(value: V | null | undefined, defaultKey: string | undefined): string | undefined; | ||
getKey<Value extends V, DefaultKey extends StringKeyOf<E>>(value: (Value & StrictEnumParam<V, Value>) | null | undefined, defaultKey?: DefaultKey): StringKeyOfType<E, Value> | DefaultKey | undefined; | ||
/** | ||
* Gets the enum value for the provided key. | ||
* Throws an error if the provided key is invalid. | ||
* @param key - A potential key value for this enum. | ||
* @return The enum value for the provided key. | ||
* @throws {Error} if the provided string is not a valid key for this enum. | ||
* Performs a strict lookup of enum value by key. | ||
* This method is as strict as possible with compile-time types and run-time | ||
* validation for a lookup based on a (expected to be) valid key that is | ||
* guaranteed to return a valid value. | ||
* @param key - A valid key for this enum. | ||
* @return The value for the provided key. | ||
* @throws {Error} if the provided key is invalid for this enum. | ||
*/ | ||
getValueOrThrow(key: string | null | undefined): T[StringKeyOf<T>]; | ||
getValue<K extends StringKeyOf<E>>(key: K): E[K]; | ||
/** | ||
* Gets the enum value for the provided key. | ||
* Returns a default value if the provided key is invalid. | ||
* @param key - A potential key value for this enum. | ||
* @param defaultValue - The value to be returned if the provided key is invalid. | ||
* @return The enum value for the provided key. | ||
* Returns `defaultValue` if the provided key is invalid. | ||
* Performs a strict lookup of enum value by key, with a default value | ||
* returned if the provided key is null/undefined. | ||
* This method is as strict as possible with compile-time types and run-time | ||
* validation for a lookup based on a (expected to be) valid key that is | ||
* guaranteed to return a valid value. | ||
* Throws an error if the provided key is non-null and invalid. | ||
* @param key - A valid key for this enum (or null/undefined). | ||
* @return The value for the provided key, or undefined. | ||
* @throws {Error} if the provided key is invalid for this enum. | ||
*/ | ||
getValueOrDefault(key: string | null | undefined, defaultValue: T[StringKeyOf<T>]): T[StringKeyOf<T>]; | ||
getValue<K extends StringKeyOf<E>>(key: K | null | undefined): E[K] | undefined; | ||
/** | ||
* Gets the enum value for the provided key. | ||
* Returns a default value if the provided key is invalid. | ||
* @param key - A potential key value for this enum. | ||
* @param defaultValue - The value to be returned if the provided key is invalid. | ||
* @return The enum value for the provided key. | ||
* Returns `defaultValue` if the provided key is invalid. | ||
* Performs a strict lookup of enum value by key, with a default value | ||
* returned if the provided key is null/undefined. | ||
* This method is as strict as possible with compile-time types and run-time | ||
* validation for a lookup based on a (expected to be) valid key that is | ||
* guaranteed to return a valid value. | ||
* Throws an error if the provided key is invalid. | ||
* @param key - A valid key for this enum (or null/undefined). | ||
* @param defaultValue - A valid value to be returned if the key is null/undefined. | ||
* @return The value for the provided key, or the default value. | ||
* @throws {Error} if the provided key is invalid for this enum. | ||
*/ | ||
getValueOrDefault(key: string | null | undefined, defaultValue?: T[StringKeyOf<T>]): T[StringKeyOf<T>] | undefined; | ||
getValue<K extends StringKeyOf<E>>(key: K | null | undefined, defaultValue: undefined): E[K] | undefined; | ||
/** | ||
* Gets the enum value for the provided key. | ||
* Returns a default value if the provided key is invalid. | ||
* @param key - A potential key value for this enum. | ||
* @param defaultValue - The value to be returned if the provided key is invalid. | ||
* @return The enum value for the provided key. | ||
* Returns `defaultValue` if the provided key is invalid. | ||
* Performs a strict lookup of enum value by key, with a default value | ||
* returned if the provided key is null/undefined. | ||
* This method is as strict as possible with compile-time types and run-time | ||
* validation for a lookup based on a (expected to be) valid key that is | ||
* guaranteed to return a valid value. | ||
* Throws an error if the provided key is invalid. | ||
* @param key - A valid key for this enum (or null/undefined). | ||
* @param defaultValue - A valid value to be returned if the key is null/undefined. | ||
* @return The value for the provided key, or the default value. | ||
* @throws {Error} if the provided key is invalid for this enum. | ||
*/ | ||
getValueOrDefault(key: string | null | undefined, defaultValue: V): V; | ||
getValue<K extends StringKeyOf<E>, DefaultValue extends V>(key: K | null | undefined, defaultValue: DefaultValue & StrictEnumParam<V, DefaultValue>): E[K] | DefaultValue; | ||
/** | ||
* Gets the enum value for the provided key. | ||
* Returns a default value if the provided key is invalid. | ||
* @param key - A potential key value for this enum. | ||
* @param defaultValue - The value to be returned if the provided key is invalid. | ||
* @return The enum value for the provided key. | ||
* Returns `defaultValue` if the provided key is invalid. | ||
* Performs a strict lookup of enum value by key, with a default value | ||
* returned if the provided key is null/undefined. | ||
* This method is as strict as possible with compile-time types and run-time | ||
* validation for a lookup based on a (expected to be) valid key that is | ||
* guaranteed to return a valid value. | ||
* Throws an error if the provided key is invalid. | ||
* @param key - A valid key for this enum (or null/undefined). | ||
* @param defaultValue - A valid value to be returned if the key is null/undefined. | ||
* @return The value for the provided key, or the default value. | ||
* @throws {Error} if the provided key is invalid for this enum. | ||
*/ | ||
getValueOrDefault(key: string | null | undefined, defaultValue: V | undefined): V | undefined; | ||
getValue<K extends StringKeyOf<E>, DefaultValue extends V>(key: K | null | undefined, defaultValue?: DefaultValue & StrictEnumParam<V, DefaultValue>): E[K] | DefaultValue | undefined; | ||
} | ||
@@ -367,5 +402,21 @@ export declare namespace EnumWrapper { | ||
* A tuple containing the key and value of a single entry in an enum. | ||
* @template T - Type of an enum-like object. | ||
* | ||
* @template E - The type of the enum-like object that is wrapped by the EnumWrapper.. | ||
* @template V - The enum value type (always allow this to default!). | ||
* NOTE: This template param shouldn't need to exist. It is a workaround | ||
* to a TypeScript design limitation. | ||
* See: https://github.com/microsoft/TypeScript/issues/35322#issuecomment-558247434 | ||
*/ | ||
type Entry<T extends Record<StringKeyOf<T>, number | string> = any> = Readonly<[StringKeyOf<T>, T[StringKeyOf<T>]]>; | ||
type Entry<E extends Record<StringKeyOf<E>, V>, V extends number | string = E[StringKeyOf<E>]> = Readonly<[StringKeyOf<E>, V]>; | ||
namespace Entry { | ||
/** | ||
* Helper type for defining a generalized EnumWrapper.Entry type for any kind | ||
* of enum whose values are assignable to type T. | ||
* | ||
* Example: | ||
* // Function that works with an EnumWrapper.Entry for any numeric enum | ||
* function foo(entry: EnumWrapper.Entry.OfType<number>): void; | ||
*/ | ||
type OfType<T extends number | string> = Entry<EnumWrapper.OfType<T>>; | ||
} | ||
/** | ||
@@ -380,73 +431,32 @@ * A function used in iterating all key/value entries in an enum. | ||
* @template R - The type of the result. | ||
* @template V - Type of the enum value. | ||
* @template T - Type of an enum-like object. | ||
* @template E - The type of the enum-like object that is wrapped by the EnumWrapper.. | ||
* @template V - The enum value type (always allow this to default!). | ||
* NOTE: This template param shouldn't need to exist. It is a workaround | ||
* to a TypeScript design limitation. | ||
* See: https://github.com/microsoft/TypeScript/issues/35322#issuecomment-558247434 | ||
*/ | ||
type Iteratee<R = any, V extends number | string = number | string, T extends Record<StringKeyOf<T>, V> = any> = (this: any, value: T[StringKeyOf<T>], key: StringKeyOf<T>, enumWrapper: EnumWrapper<V, T>, index: number) => R; | ||
} | ||
/** | ||
* Type alias for an {@link EnumWrapper} for any type of enum-like object that contains only number values. | ||
* | ||
* @template T - Type of an enum-like object that contains only number values. | ||
*/ | ||
export declare type NumberEnumWrapper<T extends Record<StringKeyOf<T>, number> = any> = EnumWrapper<number, T>; | ||
export declare namespace NumberEnumWrapper { | ||
type Iteratee<R, E extends Record<StringKeyOf<E>, V>, V extends number | string = E[StringKeyOf<E>]> = (this: any, value: V, key: StringKeyOf<E>, enumWrapper: EnumWrapper<E, V>, index: number) => R; | ||
namespace Iteratee { | ||
/** | ||
* Helper type for defining a generalized EnumWrapper.Iteratee type that | ||
* returns type R for any kind of enum whose values are assignable to type T. | ||
* | ||
* Example: | ||
* // Function that works with an EnumWrapper.Iteratee for any numeric enum | ||
* // and returns a boolean value. | ||
* function foo(iteratee: EnumWrapper.Entry.OfType<boolean, number>): void; | ||
*/ | ||
type OfType<R, T extends number | string = number | string> = Iteratee<R, EnumWrapper.OfType<T>>; | ||
} | ||
/** | ||
* Type alias for an {@link EnumWrapper.Entry} for any type of enum-like object that contains only number values. | ||
* Helper type for defining a generalized EnumWrapper type for any kind | ||
* of enum whose values are assignable to type T. | ||
* | ||
* @template T - Type of an enum-like object that contains only number values. | ||
* Example: | ||
* // Function that works with an EnumWrapper for any numeric enum | ||
* function foo(enumWrapper: EnumWrapper.OfType<number>): void; | ||
*/ | ||
type Entry<T extends Record<StringKeyOf<T>, number> = any> = EnumWrapper.Entry<T>; | ||
/** | ||
* Type alias for an {@link EnumWrapper.Iteratee} for any type of enum-like object that contains only number values. | ||
* | ||
* @template R - The type of the result. | ||
* @template T - Type of an enum-like object that contains only number values. | ||
*/ | ||
type Iteratee<R = any, T extends Record<StringKeyOf<T>, number> = any> = EnumWrapper.Iteratee<R, number, T>; | ||
type OfType<T extends number | string> = EnumWrapper<Record<string, T>>; | ||
} | ||
/** | ||
* Type alias for an {@link EnumWrapper} for any type of enum-like object that contains only string values. | ||
* | ||
* @template T - Type of an enum-like object that contains only string values. | ||
*/ | ||
export declare type StringEnumWrapper<T extends Record<StringKeyOf<T>, string> = any> = EnumWrapper<string, T>; | ||
export declare namespace StringEnumWrapper { | ||
/** | ||
* Type alias for an {@link EnumWrapper.Entry} for any type of enum-like object that contains only string values. | ||
* | ||
* @template T - Type of an enum-like object that contains only string values. | ||
*/ | ||
type Entry<T extends Record<StringKeyOf<T>, string> = any> = EnumWrapper.Entry<T>; | ||
/** | ||
* Type alias for an {@link EnumWrapper.Iteratee} for any type of enum-like object that contains only string values. | ||
* | ||
* @template R - The type of the result. | ||
* @template T - Type of an enum-like object that contains only string values. | ||
*/ | ||
type Iteratee<R = any, T extends Record<StringKeyOf<T>, string> = any> = EnumWrapper.Iteratee<R, string, T>; | ||
} | ||
/** | ||
* Type alias for an {@link EnumWrapper} for any type of enum-like object that contains a mix of | ||
* number and string values. | ||
* | ||
* @template T - Type of an enum-like object that contains a mix of number and string values. | ||
*/ | ||
export declare type MixedEnumWrapper<T extends Record<StringKeyOf<T>, number | string> = any> = EnumWrapper<number | string, T>; | ||
export declare namespace MixedEnumWrapper { | ||
/** | ||
* Type alias for an {@link EnumWrapper.Entry} for any type of enum-like object that contains a mix of | ||
* number and string values. | ||
* | ||
* @template T - Type of an enum-like object that contains a mix of number and string values. | ||
*/ | ||
type Entry<T extends Record<StringKeyOf<T>, number | string> = any> = EnumWrapper.Entry<T>; | ||
/** | ||
* Type alias for an {@link EnumWrapper.Iteratee} for any type of enum-like object that contains a mix of | ||
* number and string values. | ||
* | ||
* @template R - The type of the result. | ||
* @template T - Type of an enum-like object that contains a mix of number and string values. | ||
*/ | ||
type Iteratee<R = any, T extends Record<StringKeyOf<T>, number | string> = any> = EnumWrapper.Iteratee<R, number | string, T>; | ||
} | ||
export {}; | ||
//# sourceMappingURL=EnumWrapper.d.ts.map |
@@ -0,1 +1,2 @@ | ||
export * from "./EnumObject"; | ||
export * from "./EnumWrapper"; | ||
@@ -2,0 +3,0 @@ export * from "./EnumValueVisitor"; |
@@ -7,2 +7,9 @@ /** | ||
export declare type StringKeyOf<T> = Extract<keyof T, string>; | ||
/** | ||
* Extracts only keys of type T that are assignable to type `string` | ||
* and whose property values are asslignable to type V. | ||
*/ | ||
export declare type StringKeyOfType<T, V> = { | ||
[P in StringKeyOf<T>]: T[P] extends V ? P : never; | ||
}[StringKeyOf<T>]; | ||
//# sourceMappingURL=types.d.ts.map |
{ | ||
"name": "ts-enum-util", | ||
"version": "4.0.1", | ||
"version": "5.0.0-beta.0", | ||
"description": "TypeScript Enum Utilities", | ||
@@ -37,4 +37,7 @@ "repository": { | ||
"jest:coverage": "npm run clean:coverage && jest --coverage", | ||
"dtslint:v2_9_plus": "dtslint type_tests/v2_9_plus", | ||
"dtslint": "run-s clean:dist build:types dtslint:v2_9_plus", | ||
"dtslint:v2_9_plus": "dtslint --expectOnly type_tests/v2_9_plus", | ||
"dtslint:v3_2_plus": "dtslint --expectOnly type_tests/v3_2_plus", | ||
"dtslint:v3_7_plus": "dtslint --expectOnly type_tests/v3_7_plus", | ||
"dtslint:all": "run-s dtslint:v2_9_plus dtslint:v3_2_plus dtslint:v3_7_plus", | ||
"dtslint": "run-s clean:dist build:types dtslint:all", | ||
"test": "run-s compile prettier:test lint dtslint jest", | ||
@@ -51,14 +54,13 @@ "test:coverage": "run-s compile prettier:test lint dtslint jest:coverage", | ||
"devDependencies": { | ||
"@types/jest": "24.0.9", | ||
"@types/node": "10.12.18", | ||
"coveralls": "3.0.3", | ||
"dtslint": "0.5.3", | ||
"jest": "24.1.0", | ||
"@types/jest": "24.0.23", | ||
"coveralls": "3.0.9", | ||
"dtslint": "2.0.2", | ||
"jest": "24.9.0", | ||
"npm-run-all": "4.1.5", | ||
"prettier": "1.16.4", | ||
"rimraf": "2.6.3", | ||
"ts-jest": "24.0.0", | ||
"tslint": "5.13.1", | ||
"prettier": "1.19.1", | ||
"rimraf": "3.0.0", | ||
"ts-jest": "24.2.0", | ||
"tslint": "5.20.1", | ||
"tslint-config-prettier": "1.18.0", | ||
"typescript": "3.3.3333" | ||
"typescript": "3.7.2" | ||
}, | ||
@@ -65,0 +67,0 @@ "peerDependencies": { |
@@ -152,3 +152,2 @@ [![npm version](https://img.shields.io/npm/v/ts-enum-util.svg)](https://www.npmjs.com/package/ts-enum-util) | ||
must exist (either natively or via polyfill) in the run-time environment: | ||
- `Map` | ||
- `WeakMap` | ||
@@ -155,0 +154,0 @@ - `Symbol` |
import { EnumWrapper } from "./EnumWrapper"; | ||
import { StringKeyOf } from "./types"; | ||
import { EnumObject } from "./EnumObject"; | ||
import * as symbols from "./symbols"; | ||
@@ -19,6 +19,6 @@ import { visitEnumValue } from "./visitEnumValue"; | ||
*/ | ||
const enumWrapperInstancesCache = new WeakMap<object, EnumWrapper>(); | ||
const enumWrapperInstancesCache = new WeakMap<object, EnumWrapper<any>>(); | ||
/** | ||
* Gets a cached EnumWrapper for an enum-like object with number values. | ||
* Gets a cached EnumWrapper for an enum-like object. | ||
* Creates and caches a new EnumWrapper if one is not already cached. | ||
@@ -28,33 +28,7 @@ * @param enumObj - An enum-like object with number values. | ||
* | ||
* @template T - Type of the enum-like object that is being wrapped. | ||
* @template E - Type of the enum-like object that is being wrapped. | ||
*/ | ||
export function $enum< | ||
V extends number, | ||
T extends Record<StringKeyOf<T>, number> | ||
>(enumObj: T): EnumWrapper<number, T>; | ||
/** | ||
* Gets a cached EnumWrapper for an enum-like object with string values. | ||
* Creates and caches a new EnumWrapper if one is not already cached. | ||
* @param enumObj - An enum-like object with string values. | ||
* @return An instance of EnumWrapper for the provided enumObj. | ||
* | ||
* @template T - Type of the enum-like object that is being wrapped. | ||
*/ | ||
export function $enum<T extends Record<StringKeyOf<T>, string>>( | ||
enumObj: T | ||
): EnumWrapper<string, T>; | ||
/** | ||
* Gets a cached EnumWrapper for an enum-like object with a mixture of number | ||
* and string values. | ||
* Creates and caches a new EnumWrapper if one is not already cached. | ||
* @param enumObj - An enum-like object with a mixture of number and string | ||
* values. | ||
* @return An instance of EnumWrapper for the provided enumObj. | ||
* | ||
* @template T - Type of the enum-like object that is being wrapped. | ||
*/ | ||
export function $enum<T extends Record<StringKeyOf<T>, number | string>>( | ||
enumObj: T | ||
): EnumWrapper<number | string, T>; | ||
export function $enum(enumObj: object): EnumWrapper { | ||
export function $enum<E extends EnumObject.Constraint<E>>( | ||
enumObj: E | ||
): EnumWrapper<E> { | ||
let result = enumWrapperInstancesCache.get(enumObj); | ||
@@ -61,0 +35,0 @@ |
@@ -1,3 +0,6 @@ | ||
import { createUnhandledEntryError } from "./createUnhandledEntryError"; | ||
import { | ||
createUnhandledEntryError, | ||
createUnexpectedValueError | ||
} from "./errorUtil"; | ||
import { | ||
EnumValueMapperCore, | ||
@@ -51,3 +54,3 @@ EnumValueMapper, | ||
} else { | ||
throw new Error(`Unexpected value: ${this.value}`); | ||
throw createUnexpectedValueError(this.value); | ||
} | ||
@@ -93,3 +96,3 @@ } | ||
} else { | ||
throw new Error(`Unexpected value: null`); | ||
throw createUnexpectedValueError(null); | ||
} | ||
@@ -135,3 +138,3 @@ } | ||
} else { | ||
throw new Error(`Unexpected value: undefined`); | ||
throw createUnexpectedValueError(undefined); | ||
} | ||
@@ -138,0 +141,0 @@ } |
@@ -16,3 +16,3 @@ import { | ||
export type EnumValueMapperCore<E extends string | number, T> = { | ||
[P in E]: T | typeof unhandledEntry | ||
[P in E]: T | typeof unhandledEntry; | ||
}; | ||
@@ -19,0 +19,0 @@ |
@@ -7,5 +7,7 @@ import { | ||
} from "./symbols"; | ||
import { createUnhandledEntryError } from "./createUnhandledEntryError"; | ||
import { | ||
WidenEnumType, | ||
createUnhandledEntryError, | ||
createUnexpectedValueError | ||
} from "./errorUtil"; | ||
import { | ||
EnumValueVisitorHandler, | ||
@@ -20,6 +22,6 @@ EnumValueVisitorCore, | ||
/** | ||
* A wrapper around a string literal or string enum value to be visited. | ||
* A wrapper around an enum or string/number literal value to be visited. | ||
* Do not use this class directly. Use the {@link visitString} function to get an instance of this class. | ||
* | ||
* @template E - A string literal type or string enum type. | ||
* @template E - An enum type or string/number literal union type. | ||
*/ | ||
@@ -47,6 +49,5 @@ export class EnumValueVisitee<E extends string | number> { | ||
} else if (visitor[handleUnexpected]) { | ||
return processEntry(visitor[handleUnexpected]!, (this | ||
.value as any) as WidenEnumType<E>); | ||
return processEntry(visitor[handleUnexpected]!, this.value); | ||
} else { | ||
throw new Error(`Unexpected value: ${this.value}`); | ||
throw createUnexpectedValueError(this.value); | ||
} | ||
@@ -57,3 +58,3 @@ } | ||
/** | ||
* A wrapper around a string literal or string enum value to be visited. | ||
* A wrapper around an enum or string/number literal value to be visited. | ||
* For values that may be null. | ||
@@ -66,3 +67,3 @@ * Do not use this class directly. Use the {@link visitString} function to get an instance of this class. | ||
* | ||
* @template E - A string literal type or string enum type. | ||
* @template E - An enum type or string/number literal union type. | ||
*/ | ||
@@ -91,3 +92,3 @@ export class EnumValueVisiteeWithNull<E extends string | number> { | ||
} else { | ||
throw new Error(`Unexpected value: null`); | ||
throw createUnexpectedValueError(null); | ||
} | ||
@@ -98,3 +99,3 @@ } | ||
/** | ||
* A wrapper around a string literal or string enum value to be visited. | ||
* A wrapper around an enum or string/number literal value to be visited. | ||
* For values that may be undefined. | ||
@@ -107,3 +108,3 @@ * Do not use this class directly. Use the {@link visitString} function to get an instance of this class. | ||
* | ||
* @template E - A string literal type or string enum type. | ||
* @template E - An enum type or string/number literal union type. | ||
*/ | ||
@@ -132,3 +133,3 @@ export class EnumValueVisiteeWithUndefined<E extends string | number> { | ||
} else { | ||
throw new Error(`Unexpected value: undefined`); | ||
throw createUnexpectedValueError(undefined); | ||
} | ||
@@ -139,3 +140,3 @@ } | ||
/** | ||
* A wrapper around a string literal or string enum value to be visited. | ||
* A wrapper around an enum or string/number literal value to be visited. | ||
* For values that may be null and undefined. | ||
@@ -149,3 +150,3 @@ * Do not use this class directly. Use the {@link visitString} function to get an instance of this class. | ||
* | ||
* @template E - A string literal type or string enum type. | ||
* @template E - An enum type or string/number literal union type. | ||
*/ | ||
@@ -152,0 +153,0 @@ export declare class EnumValueVisiteeWithNullAndUndefined< |
@@ -9,11 +9,4 @@ import { | ||
/** | ||
* Helper type to widen a number/string enum/literal type to plain string or number. | ||
*/ | ||
export type WidenEnumType<E extends number | string> = | ||
| (E extends number ? number : never) | ||
| (E extends string ? string : never); | ||
/** | ||
* Generic method signature for a string visitor handler method. | ||
* @template E - The type of the parameter to the handler. Must be a string literal, null, or undefined. | ||
* Generic method signature for a value visitor handler method. | ||
* @template T - The type of the value. | ||
* @template R - The return type of the handler. Defaults to void. | ||
@@ -23,19 +16,28 @@ * @param value - The value being visited by the visitor. | ||
*/ | ||
export type EnumValueVisitorHandler< | ||
E extends string | number | null | undefined, | ||
R = void | ||
> = (value: E) => R; | ||
export type EnumValueVisitorHandler<T, R = void> = (value: T) => R; | ||
/** | ||
* Core definition of all string visitor interfaces. | ||
* Core definition of all value visitor interfaces. | ||
* Defines visitor handler properties for each possible value of type `E`. | ||
* | ||
* @template E - A string literal type or string enum type. | ||
* @template E - An enum type or string/number literal union type. | ||
* @template R - The return type of the visitor methods. | ||
*/ | ||
export type EnumValueVisitorCore<E extends string | number, R> = { | ||
[P in E]: EnumValueVisitorHandler<P, R> | typeof unhandledEntry | ||
[P in E]: EnumValueVisitorHandler<P, R> | typeof unhandledEntry; | ||
}; | ||
/** | ||
* A visitor interface for visiting an unexpected value. | ||
* This is never used by itself, but combined with {@link EnumValueVisitor} as needed. | ||
* | ||
* @template R - The return type of the visitor method. | ||
*/ | ||
export interface UnexpectedEnumValueVisitor<R> { | ||
[handleUnexpected]?: | ||
| EnumValueVisitorHandler<any, R> | ||
| typeof unhandledEntry; | ||
} | ||
/** | ||
* A visitor interface for visiting a null value. | ||
@@ -63,5 +65,5 @@ * This is never used by itself, but combined with {@link EnumValueVisitor} as needed. | ||
/** | ||
* A visitor interface for visiting the value of a string literal type or a string enum type. | ||
* A visitor interface for visiting the value of an enum type or string/number literal union type. | ||
* | ||
* @template E - A string literal type or string enum type. | ||
* @template E - An enum type or string/number literal union type. | ||
* @template R - The return type of the visitor methods. | ||
@@ -72,13 +74,9 @@ */ | ||
R | ||
> = EnumValueVisitorCore<E, R> & { | ||
[handleUnexpected]?: | ||
| EnumValueVisitorHandler<WidenEnumType<E> | null | undefined, R> | ||
| typeof unhandledEntry; | ||
}; | ||
> = EnumValueVisitorCore<E, R> & UnexpectedEnumValueVisitor<R>; | ||
/** | ||
* Combines {@link EnumValueVisitor} with {@link NullEnumValueVisitor} for visiting a string literal/enum | ||
* that may be null. | ||
* Combines {@link EnumValueVisitor} with {@link NullEnumValueVisitor} for | ||
* visiting an enum or string/number literal value that may be null. | ||
* | ||
* @template E - A string literal type or string enum type. | ||
* @template E - An enum type or string/number literal union type. | ||
* @template R - The return type of the visitor methods. | ||
@@ -90,13 +88,10 @@ */ | ||
> = EnumValueVisitorCore<E, R> & | ||
NullEnumValueVisitor<R> & { | ||
[handleUnexpected]?: | ||
| EnumValueVisitorHandler<WidenEnumType<E> | undefined, R> | ||
| typeof unhandledEntry; | ||
}; | ||
NullEnumValueVisitor<R> & | ||
UnexpectedEnumValueVisitor<R>; | ||
/** | ||
* Combines {@link EnumValueVisitor} with {@link UndefinedEnumValueVisitor} for visiting a string literal/enum | ||
* that may be undefined. | ||
* Combines {@link EnumValueVisitor} with {@link UndefinedEnumValueVisitor} for | ||
* visiting an enum or string/number literal value that may be undefined. | ||
* | ||
* @template E - A string literal type or string enum type. | ||
* @template E - An enum type or string/number literal union type. | ||
* @template R - The return type of the visitor methods. | ||
@@ -108,13 +103,11 @@ */ | ||
> = EnumValueVisitorCore<E, R> & | ||
UndefinedEnumValueVisitor<R> & { | ||
[handleUnexpected]?: | ||
| EnumValueVisitorHandler<WidenEnumType<E> | null, R> | ||
| typeof unhandledEntry; | ||
}; | ||
UndefinedEnumValueVisitor<R> & | ||
UnexpectedEnumValueVisitor<R>; | ||
/** | ||
* Combines {@link EnumValueVisitor} with {@link NullEnumValueVisitor} and {@link UndefinedEnumValueVisitor} | ||
* for visiting a string literal/enum that may be null or undefined. | ||
* Combines {@link EnumValueVisitor} with {@link NullEnumValueVisitor} and | ||
* {@link UndefinedEnumValueVisitor} for visiting an enum or string/number literal value | ||
* that may be null or undefined. | ||
* | ||
* @template E - A string literal type or string enum type. | ||
* @template E - An enum type or string/number literal union type. | ||
* @template R - The return type of the visitor methods. | ||
@@ -127,6 +120,3 @@ */ | ||
NullEnumValueVisitor<R> & | ||
UndefinedEnumValueVisitor<R> & { | ||
[handleUnexpected]?: | ||
| EnumValueVisitorHandler<WidenEnumType<E>, R> | ||
| typeof unhandledEntry; | ||
}; | ||
UndefinedEnumValueVisitor<R> & | ||
UnexpectedEnumValueVisitor<R>; |
@@ -1,8 +0,58 @@ | ||
import { StringKeyOf } from "./types"; | ||
import { | ||
isNonNumericKey, | ||
getOwnEnumerableNonNumericKeys | ||
} from "./objectKeysUtil"; | ||
import { StringKeyOf, StringKeyOfType } from "./types"; | ||
import { getOwnEnumerableNonNumericKeys } from "./objectKeysUtil"; | ||
/** | ||
* Use StrictEnumParam to define the type of a function | ||
* parameter that should be strictly assignable to a numeric enum | ||
* type. This prevents arbitrary numbers from being passed in to | ||
* the parameter, working around TypeScript's intentional decision | ||
* to allow type `number` to be assignable to all numeric enum types. | ||
* | ||
* Instead of writing a function signature as: | ||
* function doSomething(value: MyEnum): void; | ||
* | ||
* Write it like this: | ||
* function doSomething<Value extends MyEnum>( | ||
* value: StrictEnumParam<MyEnum, Value> | ||
* ): void; | ||
* | ||
* StrictEnumParam<MyEnum, Value> will evaluate to `never` | ||
* for any type `Value` that is not strictly assignable to `MyEnum` | ||
* (e.g., type `number`, or any number literal type that is not one | ||
* of the valid values for `MyEnum`), and will produce a compiler | ||
* error such as: | ||
* "Argument of type `number` is not assignable to parameter of type `never`" | ||
* | ||
* LIMITATION: | ||
* This only works for a special subset of numeric enums that are considered | ||
* "Union Enums". For an enum to be compatible, it basically must be a simple | ||
* numeric enum where every member has either an inferred value | ||
* (previous enum member + 1), or a number literal (1, 42, -3, etc.). | ||
* | ||
* If the `Enum` type argument is not a "Union Enum", then this type resolves | ||
* to simply type `Enum` and the use of StrictEnumParam is neither | ||
* beneficial nor detrimental. | ||
*/ | ||
export type StrictEnumParam< | ||
Enum extends number | string, | ||
Param extends Enum | ||
> = true extends ({ [key: number]: false } & { [P in Enum]: true })[Extract< | ||
Enum, | ||
number | ||
>] | ||
? true extends ({ [key: number]: false } & { [P in Enum]: true })[Param] | ||
? Param | ||
: never | ||
: Param; | ||
/** | ||
* Widens types that are assignable to number/string to the full number/string type. | ||
*/ | ||
type Widen<T extends number | string> = T extends number | ||
? number | ||
: T extends string | ||
? string | ||
: T; | ||
/** | ||
* A generic wrapper for any enum-like object. | ||
@@ -12,16 +62,22 @@ * Provides utilities for runtime processing of an enum's values and keys, with strict compile-time | ||
* | ||
* EnumWrapper cannot be directly instantiated. Use {@link $enum} to get/create an EnumWrapper | ||
* instance. | ||
* EnumWrapper should generally not be directly instantiated. | ||
* Use {@link $enum} to get/create an EnumWrapper instance. | ||
* | ||
* @template V - Type of the enum value. | ||
* @template T - Type of the enum-like object that is being wrapped. | ||
* @template E - The type of the enum-like object that is wrapped by the EnumWrapper.. | ||
* @template V - The enum value type (always allow this to default!). | ||
* NOTE: This template param shouldn't need to exist. It is a workaround | ||
* to a TypeScript design limitation. | ||
* See: https://github.com/microsoft/TypeScript/issues/35322#issuecomment-558247434 | ||
*/ | ||
export class EnumWrapper< | ||
V extends number | string = number | string, | ||
T extends Record<StringKeyOf<T>, V> = any | ||
> implements Iterable<EnumWrapper.Entry<T>>, ArrayLike<EnumWrapper.Entry<T>> { | ||
E extends Record<StringKeyOf<E>, V>, | ||
V extends string | number = E[StringKeyOf<E>] | ||
> | ||
implements | ||
Iterable<EnumWrapper.Entry<E, V>>, | ||
ArrayLike<EnumWrapper.Entry<E, V>> { | ||
/** | ||
* List of all keys for this enum, in the original defined order of the enum. | ||
*/ | ||
private readonly keysList: ReadonlyArray<StringKeyOf<T>>; | ||
private readonly keysList: ReadonlyArray<StringKeyOf<E>>; | ||
@@ -31,14 +87,5 @@ /** | ||
*/ | ||
private readonly valuesList: ReadonlyArray<T[StringKeyOf<T>]>; | ||
private readonly valuesList: ReadonlyArray<V>; | ||
/** | ||
* Map of enum value -> enum key. | ||
* Used for reverse key lookups. | ||
* NOTE: Performance tests show that using a Map (even if it's a slow polyfill) is faster than building a lookup | ||
* string key for values and using a plain Object: | ||
* {@link https://www.measurethat.net/Benchmarks/Show/2514/1/map-keyed-by-string-or-number} | ||
*/ | ||
private readonly keysByValueMap: ReadonlyMap<V, StringKeyOf<T>>; | ||
/** | ||
* The number of entries in this enum. | ||
@@ -59,3 +106,3 @@ * Part of the Map-like interface. | ||
*/ | ||
readonly [key: number]: EnumWrapper.Entry<T>; | ||
readonly [key: number]: EnumWrapper.Entry<E, V>; | ||
@@ -69,3 +116,3 @@ /** | ||
*/ | ||
public constructor(private readonly enumObj: T) { | ||
public constructor(enumObj: E) { | ||
// Include only own enumerable keys that are not numeric. | ||
@@ -77,4 +124,3 @@ // This is necessary to ignore the reverse-lookup entries that are automatically added | ||
const length = this.keysList.length; | ||
const valuesList = new Array<T[StringKeyOf<T>]>(length); | ||
const keysByValueMap = new Map<V, StringKeyOf<T>>(); | ||
const valuesList = new Array<V>(length); | ||
@@ -88,3 +134,2 @@ // According to multiple tests found on jsperf.com, a plain for loop is faster than using | ||
valuesList[index] = value; | ||
keysByValueMap.set(value, key); | ||
// Type casting of "this" necessary to bypass readonly index signature for initialization. | ||
@@ -95,3 +140,2 @@ (this as any)[index] = Object.freeze([key, value]); | ||
this.valuesList = Object.freeze(valuesList); | ||
this.keysByValueMap = keysByValueMap; | ||
this.size = this.length = length; | ||
@@ -103,6 +147,2 @@ | ||
public get [Symbol.toStringTag](): string { | ||
return "EnumWrapper"; | ||
} | ||
/** | ||
@@ -125,3 +165,3 @@ * @return "[object EnumWrapper]" | ||
*/ | ||
public keys(): IterableIterator<StringKeyOf<T>> { | ||
public keys(): IterableIterator<StringKeyOf<E>> { | ||
let index = 0; | ||
@@ -132,3 +172,3 @@ | ||
const isDone = index >= this.length; | ||
const result: IteratorResult<StringKeyOf<T>> = { | ||
const result: IteratorResult<StringKeyOf<E>> = { | ||
done: isDone, | ||
@@ -143,3 +183,3 @@ value: this.keysList[index] | ||
[Symbol.iterator](): IterableIterator<StringKeyOf<T>> { | ||
[Symbol.iterator](): IterableIterator<StringKeyOf<E>> { | ||
return this; | ||
@@ -158,23 +198,26 @@ } | ||
*/ | ||
public values(): IterableIterator<T[StringKeyOf<T>]> { | ||
let index = 0; | ||
public readonly values: () => IterableIterator<V> = | ||
Symbol.iterator in Array.prototype | ||
? () => this.valuesList[Symbol.iterator]() | ||
: () => { | ||
let index = 0; | ||
return { | ||
next: () => { | ||
const isDone = index >= this.length; | ||
const result: IteratorResult<T[StringKeyOf<T>]> = { | ||
done: isDone, | ||
value: this.valuesList[index] | ||
}; | ||
return { | ||
next: () => { | ||
const isDone = index >= this.length; | ||
const result: IteratorResult<V> = { | ||
done: isDone, | ||
value: this.valuesList[index] | ||
}; | ||
++index; | ||
++index; | ||
return result; | ||
}, | ||
return result; | ||
}, | ||
[Symbol.iterator](): IterableIterator<T[StringKeyOf<T>]> { | ||
return this; | ||
} | ||
}; | ||
} | ||
[Symbol.iterator](): IterableIterator<V> { | ||
return this; | ||
} | ||
}; | ||
}; | ||
@@ -186,3 +229,3 @@ /** | ||
*/ | ||
public entries(): IterableIterator<EnumWrapper.Entry<T>> { | ||
public entries(): IterableIterator<EnumWrapper.Entry<E, V>> { | ||
let index = 0; | ||
@@ -193,3 +236,3 @@ | ||
const isDone = index >= this.length; | ||
const result: IteratorResult<EnumWrapper.Entry<T>> = { | ||
const result: IteratorResult<EnumWrapper.Entry<E, V>> = { | ||
done: isDone, | ||
@@ -205,3 +248,3 @@ // NOTE: defensive copy not necessary because entries are "frozen" | ||
[Symbol.iterator](): IterableIterator<EnumWrapper.Entry<T>> { | ||
[Symbol.iterator](): IterableIterator<EnumWrapper.Entry<E, V>> { | ||
return this; | ||
@@ -217,3 +260,3 @@ } | ||
*/ | ||
public [Symbol.iterator](): IterableIterator<EnumWrapper.Entry<T>> { | ||
public [Symbol.iterator](): IterableIterator<EnumWrapper.Entry<E, V>> { | ||
return this.entries(); | ||
@@ -231,3 +274,3 @@ } | ||
public forEach( | ||
iteratee: EnumWrapper.Iteratee<void, V, T>, | ||
iteratee: EnumWrapper.Iteratee<void, E, V>, | ||
context?: any | ||
@@ -252,7 +295,7 @@ ): void { | ||
* @param context - If provided, then the iteratee will be called with the context as its "this" value. | ||
* @return A new array containg the results of the iteratee. | ||
* @return A new array containing the results of the iteratee. | ||
* | ||
* @template R - The of the mapped result for each entry. | ||
*/ | ||
public map<R>(iteratee: EnumWrapper.Iteratee<R, V, T>, context?: any): R[] { | ||
public map<R>(iteratee: EnumWrapper.Iteratee<R, E, V>, context?: any): R[] { | ||
const length = this.length; | ||
@@ -281,3 +324,3 @@ const result = new Array<R>(length); | ||
*/ | ||
public getKeys(): (StringKeyOf<T>)[] { | ||
public getKeys(): StringKeyOf<E>[] { | ||
// need to return a copy of this.keysList so it can be returned as Array instead of ReadonlyArray. | ||
@@ -294,3 +337,3 @@ return this.keysList.slice(); | ||
*/ | ||
public getValues(): T[StringKeyOf<T>][] { | ||
public getValues(): V[] { | ||
// need to return a copy of this.valuesList so it can be returned as Array instead of ReadonlyArray. | ||
@@ -305,3 +348,3 @@ return this.valuesList.slice(); | ||
*/ | ||
public getEntries(): EnumWrapper.Entry<T>[] { | ||
public getEntries(): EnumWrapper.Entry<E, V>[] { | ||
// Create an array from the indexed entries of "this". | ||
@@ -317,3 +360,3 @@ // NOTE: no need for defensive copy of each entry because all entries are "frozen". | ||
*/ | ||
public indexOfKey(key: StringKeyOf<T>): number { | ||
public indexOfKey(key: StringKeyOf<E>): number { | ||
return this.keysList.indexOf(key); | ||
@@ -327,3 +370,5 @@ } | ||
*/ | ||
public indexOfValue(value: T[StringKeyOf<T>]): number { | ||
public indexOfValue<Value extends V>( | ||
value: StrictEnumParam<V, Value> | ||
): number { | ||
return this.valuesList.indexOf(value); | ||
@@ -338,8 +383,4 @@ } | ||
*/ | ||
public isKey(key: string | null | undefined): key is StringKeyOf<T> { | ||
return ( | ||
key != null && | ||
isNonNumericKey(key) && | ||
this.enumObj.hasOwnProperty(key) | ||
); | ||
public isKey(key: string | null | undefined): key is StringKeyOf<E> { | ||
return this.keysList.indexOf(key as StringKeyOf<E>) !== -1; | ||
} | ||
@@ -354,3 +395,3 @@ | ||
*/ | ||
public asKeyOrThrow(key: string | null | undefined): StringKeyOf<T> { | ||
public asKeyOrThrow(key: string | null | undefined): StringKeyOf<E> { | ||
if (this.isKey(key)) { | ||
@@ -372,7 +413,8 @@ return key; | ||
* Returns `defaultKey` if the provided key is invalid. | ||
* @throws {Error} if `defaultKey` is not a valid key for this enum. | ||
*/ | ||
public asKeyOrDefault( | ||
key: string | null | undefined, | ||
defaultKey: StringKeyOf<T> | ||
): StringKeyOf<T>; | ||
defaultKey: StringKeyOf<E> | ||
): StringKeyOf<E>; | ||
/** | ||
@@ -385,47 +427,19 @@ * Casts a string to a properly-typed key for this enum. | ||
* Returns `defaultKey` if the provided key is invalid. | ||
* @throws {Error} if `defaultKey` is not a valid key for this enum. | ||
*/ | ||
public asKeyOrDefault( | ||
key: string | null | undefined, | ||
defaultKey?: StringKeyOf<T> | ||
): StringKeyOf<T> | undefined; | ||
/** | ||
* Casts a string to a properly-typed key for this enum. | ||
* Returns a default key if the provided key is invalid. | ||
* @param key - A potential key value for this enum. | ||
* @param defaultKey - The key to be returned if the provided key is invalid. | ||
* @return The provided key value, cast to the type of this enum's keys. | ||
* Returns `defaultKey` if the provided key is invalid. | ||
*/ | ||
defaultKey?: StringKeyOf<E> | ||
): StringKeyOf<E> | undefined; | ||
public asKeyOrDefault( | ||
key: string | null | undefined, | ||
defaultKey: string | ||
): string; | ||
/** | ||
* Casts a string to a properly-typed key for this enum. | ||
* Returns a default key if the provided key is invalid. | ||
* @param key - A potential key value for this enum. | ||
* @param defaultKey - The key to be returned if the provided key is invalid. | ||
* @return The provided key value, cast to the type of this enum's keys. | ||
* Returns `defaultKey` if the provided key is invalid. | ||
*/ | ||
public asKeyOrDefault( | ||
key: string | null | undefined, | ||
defaultKey: string | undefined | ||
): string | undefined; | ||
/** | ||
* Casts a string to a properly-typed key for this enum. | ||
* Returns a default key if the provided key is invalid. | ||
* @param key - A potential key value for this enum. | ||
* @param defaultKey - The key to be returned if the provided key is invalid. | ||
* @return The provided key value, cast to the type of this enum's keys. | ||
* Returns `defaultKey` if the provided key is invalid. | ||
*/ | ||
public asKeyOrDefault( | ||
key: string | null | undefined, | ||
defaultKey?: StringKeyOf<T> | string | ||
): string | undefined { | ||
defaultKey?: StringKeyOf<E> | ||
): StringKeyOf<E> | undefined { | ||
const verifiedDefaultKey = | ||
defaultKey != null ? this.asKeyOrThrow(defaultKey) : undefined; | ||
if (this.isKey(key)) { | ||
return key; | ||
} else { | ||
return defaultKey; | ||
return verifiedDefaultKey; | ||
} | ||
@@ -440,4 +454,8 @@ } | ||
*/ | ||
public isValue(value: V | null | undefined): value is T[StringKeyOf<T>] { | ||
return value != null && this.keysByValueMap.has(value); | ||
// HACK: The intersection in "value is Widen<V> & V" is a work around for a TS limitation. | ||
// See: https://github.com/microsoft/TypeScript/issues/35257#issuecomment-557100788 | ||
public isValue( | ||
value: Widen<V> | V | null | undefined | ||
): value is Widen<V> & V { | ||
return this.valuesList.indexOf(value as V) !== -1; | ||
} | ||
@@ -452,3 +470,3 @@ | ||
*/ | ||
public asValueOrThrow(value: V | null | undefined): T[StringKeyOf<T>] { | ||
public asValueOrThrow(value: Widen<V> | V | null | undefined): V { | ||
if (this.isValue(value)) { | ||
@@ -470,7 +488,8 @@ return value; | ||
* Returns `defaultValue` if the provided value is invalid. | ||
* @throws {Error} if `defaultValue` is not a valid value for this enum. | ||
*/ | ||
public asValueOrDefault( | ||
value: V | null | undefined, | ||
defaultValue: T[StringKeyOf<T>] | ||
): T[StringKeyOf<T>]; | ||
public asValueOrDefault<DefaultValue extends V>( | ||
value: Widen<V> | V | null | undefined, | ||
defaultValue: StrictEnumParam<V, DefaultValue> | ||
): V; | ||
/** | ||
@@ -483,44 +502,18 @@ * Casts a value to a properly-typed value for this enum. | ||
* Returns `defaultValue` if the provided value is invalid. | ||
* @throws {Error} if `defaultValue` is not a valid value for this enum. | ||
*/ | ||
public asValueOrDefault( | ||
value: V | null | undefined, | ||
defaultValue?: T[StringKeyOf<T>] | ||
): T[StringKeyOf<T>] | undefined; | ||
/** | ||
* Casts a value to a properly-typed value for this enum. | ||
* Returns a default value if the provided value is invalid. | ||
* @param value - A potential value for this enum. | ||
* @param defaultValue - The value to be returned if the provided value is invalid. | ||
* @return The provided value, cast to the type of this enum's values. | ||
* Returns `defaultValue` if the provided value is invalid. | ||
*/ | ||
public asValueOrDefault(value: V | null | undefined, defaultValue: V): V; | ||
/** | ||
* Casts a value to a properly-typed value for this enum. | ||
* Returns a default value if the provided value is invalid. | ||
* @param value - A potential value for this enum. | ||
* @param defaultValue - The value to be returned if the provided value is invalid. | ||
* @return The provided value, cast to the type of this enum's values. | ||
* Returns `defaultValue` if the provided value is invalid. | ||
*/ | ||
public asValueOrDefault( | ||
value: V | null | undefined, | ||
defaultValue: V | undefined | ||
public asValueOrDefault<DefaultValue extends V>( | ||
value: Widen<V> | V | null | undefined, | ||
defaultValue?: StrictEnumParam<V, DefaultValue> | ||
): V | undefined; | ||
/** | ||
* Casts a value to a properly-typed value for this enum. | ||
* Returns a default value if the provided value is invalid. | ||
* @param value - A potential value for this enum. | ||
* @param defaultValue - The value to be returned if the provided value is invalid. | ||
* @return The provided value, cast to the type of this enum's values. | ||
* Returns `defaultValue` if the provided value is invalid. | ||
*/ | ||
public asValueOrDefault( | ||
value: V | null | undefined, | ||
defaultValue?: T[StringKeyOf<T>] | V | ||
): V | undefined { | ||
public asValueOrDefault(value: Widen<V>, defaultValue?: V): V | undefined { | ||
const verifiedDefaultValue = | ||
defaultValue != null | ||
? this.asValueOrThrow(defaultValue) | ||
: undefined; | ||
if (this.isValue(value)) { | ||
return value; | ||
} else { | ||
return defaultValue; | ||
return verifiedDefaultValue; | ||
} | ||
@@ -530,103 +523,100 @@ } | ||
/** | ||
* Performs a reverse lookup from enum value to corresponding enum key. | ||
* Throws an error if the value is invalid. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the duplicated value is | ||
* arbitrarily returned. | ||
* @param value - A potential value for this enum. | ||
* Performs a strict reverse lookup from enum value to corresponding enum key. | ||
* This method is as strict as possible with compile-time types and run-time | ||
* validation for a lookup based on a (expected to be) valid value that is | ||
* guaranteed to return a valid key. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the | ||
* duplicated value is arbitrarily returned. | ||
* @param value - A valid value for this enum. | ||
* @return The key for the provided value. | ||
* @throws {Error} if the provided value is not a valid value for this enum. | ||
* @throws {Error} if the provided value is not valid for this enum. | ||
*/ | ||
public getKeyOrThrow(value: V | null | undefined): StringKeyOf<T> { | ||
// NOTE: Intentionally not using isValue() or asValueOrThrow() to avoid making two key lookups into the map | ||
// for successful lookups. | ||
const result = | ||
value != null ? this.keysByValueMap.get(value) : undefined; | ||
if (result != null) { | ||
return result; | ||
} else { | ||
throw new Error( | ||
`Unexpected value: ${value}. Expected one of: ${this.getValues()}` | ||
); | ||
} | ||
} | ||
public getKey<Value extends V>( | ||
value: Value & StrictEnumParam<V, Value> | ||
): StringKeyOfType<E, Value>; | ||
/** | ||
* Performs a reverse lookup from enum value to corresponding enum key. | ||
* Returns a default key if the provided value is invalid. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the duplicated value is | ||
* arbitrarily returned. | ||
* @param value - A potential value for this enum. | ||
* @param defaultKey - The key to be returned if the provided value is invalid. | ||
* Performs a strict reverse lookup from enum value to corresponding enum key. | ||
* This method is as strict as possible with compile-time types and run-time | ||
* validation for a lookup based on a (expected to be) valid value that is | ||
* guaranteed to return a valid key. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the | ||
* duplicated value is arbitrarily returned. | ||
* @param value - A valid value for this enum. | ||
* @return The key for the provided value. | ||
* Returns `defaultKey` if the provided value is invalid. | ||
* @throws {Error} if the provided value is not valid for this enum. | ||
*/ | ||
public getKeyOrDefault( | ||
value: V | null | undefined, | ||
defaultKey: StringKeyOf<T> | ||
): StringKeyOf<T>; | ||
public getKey<Value extends V>( | ||
value: (Value & StrictEnumParam<V, Value>) | null | undefined | ||
): StringKeyOfType<E, Value> | undefined; | ||
/** | ||
* Performs a reverse lookup from enum value to corresponding enum key. | ||
* Returns a default key if the provided value is invalid. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the duplicated value is | ||
* arbitrarily returned. | ||
* @param value - A potential value for this enum. | ||
* @param defaultKey - The key to be returned if the provided value is invalid. | ||
* @return The key for the provided value. | ||
* Returns `defaultKey` if the provided value is invalid. | ||
* Performs a strict reverse lookup from enum value to corresponding enum key, | ||
* with a default key to be returned if the provided value is null/undefined. | ||
* This method is as strict as possible with compile-time types and run-time | ||
* validation for a lookup based on a (expected to be) valid value that is | ||
* guaranteed to return a valid key. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the | ||
* duplicated value is arbitrarily returned. | ||
* @param value - A valid value for this enum (or null/undefined). | ||
* @param defaultKey - A valid key to be returned if the provided value is null/undefined. | ||
* @return The key for the provided value, or the default key. | ||
* @throws {Error} if the provided value or default key is not valid for this enum. | ||
*/ | ||
public getKeyOrDefault( | ||
value: V | null | undefined, | ||
defaultKey?: StringKeyOf<T> | ||
): StringKeyOf<T> | undefined; | ||
public getKey<Value extends V>( | ||
value: (Value & StrictEnumParam<V, Value>) | null | undefined, | ||
// tslint:disable-next-line:unified-signatures | ||
defaultKey: undefined | ||
): StringKeyOfType<E, Value> | undefined; | ||
/** | ||
* Performs a reverse lookup from enum value to corresponding enum key. | ||
* Returns a default key if the provided value is invalid. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the duplicated value is | ||
* arbitrarily returned. | ||
* @param value - A potential value for this enum. | ||
* @param defaultKey - The key to be returned if the provided value is invalid. | ||
* @return The key for the provided value. | ||
* Returns `defaultKey` if the provided value is invalid. | ||
* Performs a strict reverse lookup from enum value to corresponding enum key, | ||
* with a default key to be returned if the provided value is null/undefined. | ||
* This method is as strict as possible with compile-time types and run-time | ||
* validation for a lookup based on a (expected to be) valid value that is | ||
* guaranteed to return a valid key. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the | ||
* duplicated value is arbitrarily returned. | ||
* @param value - A valid value for this enum (or null/undefined). | ||
* @param defaultKey - A valid key to be returned if the provided value is null/undefined. | ||
* @return The key for the provided value, or the default key. | ||
* @throws {Error} if the provided value or default key is not valid for this enum. | ||
*/ | ||
public getKeyOrDefault( | ||
value: V | null | undefined, | ||
defaultKey: string | ||
): string; | ||
public getKey<Value extends V, DefaultKey extends StringKeyOf<E>>( | ||
value: (Value & StrictEnumParam<V, Value>) | null | undefined, | ||
defaultKey: DefaultKey | ||
): StringKeyOfType<E, Value> | DefaultKey; | ||
/** | ||
* Performs a reverse lookup from enum value to corresponding enum key. | ||
* Returns a default key if the provided value is invalid. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the duplicated value is | ||
* arbitrarily returned. | ||
* @param value - A potential value for this enum. | ||
* @param defaultKey - The key to be returned if the provided value is invalid. | ||
* @return The key for the provided value. | ||
* Returns `defaultKey` if the provided value is invalid. | ||
* Performs a strict reverse lookup from enum value to corresponding enum key, | ||
* with a default key to be returned if the provided value is null/undefined. | ||
* This method is as strict as possible with compile-time types and run-time | ||
* validation for a lookup based on a (expected to be) valid value that is | ||
* guaranteed to return a valid key. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the | ||
* duplicated value is arbitrarily returned. | ||
* @param value - A valid value for this enum (or null/undefined). | ||
* @param defaultKey - A valid key to be returned if the provided value is null/undefined. | ||
* @return The key for the provided value, or the default key. | ||
* @throws {Error} if the provided value or default key is not valid for this enum. | ||
*/ | ||
public getKeyOrDefault( | ||
public getKey<Value extends V, DefaultKey extends StringKeyOf<E>>( | ||
value: (Value & StrictEnumParam<V, Value>) | null | undefined, | ||
defaultKey?: DefaultKey | ||
): StringKeyOfType<E, Value> | DefaultKey | undefined; | ||
public getKey( | ||
value: V | null | undefined, | ||
defaultKey: string | undefined | ||
): string | undefined; | ||
/** | ||
* Performs a reverse lookup from enum value to corresponding enum key. | ||
* Returns a default key if the provided value is invalid. | ||
* NOTE: If this enum has any duplicate values, then one of the keys for the duplicated value is | ||
* arbitrarily returned. | ||
* @param value - A potential value for this enum. | ||
* @param defaultKey - The key to be returned if the provided value is invalid. | ||
* @return The key for the provided value. | ||
* Returns `defaultKey` if the provided value is invalid. | ||
*/ | ||
public getKeyOrDefault( | ||
value: V | null | undefined, | ||
defaultKey?: StringKeyOf<T> | string | ||
): string | undefined { | ||
// NOTE: Intentionally not using isValue() to avoid making two key lookups into the map for successful lookups. | ||
const result = | ||
value != null ? this.keysByValueMap.get(value) : undefined; | ||
defaultKey?: StringKeyOf<E> | ||
): StringKeyOf<E> | undefined { | ||
const verifiedDefaultKey = | ||
defaultKey != null ? this.asKeyOrThrow(defaultKey) : undefined; | ||
if (result != null) { | ||
return result; | ||
if (value == null) { | ||
return verifiedDefaultKey; | ||
} | ||
const index = this.valuesList.indexOf(value); | ||
if (index !== -1) { | ||
return this.keysList[index]; | ||
} else { | ||
return defaultKey; | ||
throw new Error( | ||
`Unexpected value: ${value}. Expected one of: ${this.getValues()}` | ||
); | ||
} | ||
@@ -636,82 +626,95 @@ } | ||
/** | ||
* Gets the enum value for the provided key. | ||
* Throws an error if the provided key is invalid. | ||
* @param key - A potential key value for this enum. | ||
* @return The enum value for the provided key. | ||
* @throws {Error} if the provided string is not a valid key for this enum. | ||
* Performs a strict lookup of enum value by key. | ||
* This method is as strict as possible with compile-time types and run-time | ||
* validation for a lookup based on a (expected to be) valid key that is | ||
* guaranteed to return a valid value. | ||
* @param key - A valid key for this enum. | ||
* @return The value for the provided key. | ||
* @throws {Error} if the provided key is invalid for this enum. | ||
*/ | ||
public getValueOrThrow(key: string | null | undefined): T[StringKeyOf<T>] { | ||
// NOTE: The key MUST be separately validated before looking up the entry in enumObj to avoid false positive | ||
// lookups for keys that match properties on Object.prototype, or keys that match the index keys of | ||
// reverse lookups on numeric enums. | ||
return this.enumObj[this.asKeyOrThrow(key)]; | ||
} | ||
public getValue<K extends StringKeyOf<E>>(key: K): E[K]; | ||
/** | ||
* Gets the enum value for the provided key. | ||
* Returns a default value if the provided key is invalid. | ||
* @param key - A potential key value for this enum. | ||
* @param defaultValue - The value to be returned if the provided key is invalid. | ||
* @return The enum value for the provided key. | ||
* Returns `defaultValue` if the provided key is invalid. | ||
* Performs a strict lookup of enum value by key, with a default value | ||
* returned if the provided key is null/undefined. | ||
* This method is as strict as possible with compile-time types and run-time | ||
* validation for a lookup based on a (expected to be) valid key that is | ||
* guaranteed to return a valid value. | ||
* Throws an error if the provided key is non-null and invalid. | ||
* @param key - A valid key for this enum (or null/undefined). | ||
* @return The value for the provided key, or undefined. | ||
* @throws {Error} if the provided key is invalid for this enum. | ||
*/ | ||
public getValueOrDefault( | ||
key: string | null | undefined, | ||
defaultValue: T[StringKeyOf<T>] | ||
): T[StringKeyOf<T>]; | ||
public getValue<K extends StringKeyOf<E>>( | ||
key: K | null | undefined | ||
): E[K] | undefined; | ||
/** | ||
* Gets the enum value for the provided key. | ||
* Returns a default value if the provided key is invalid. | ||
* @param key - A potential key value for this enum. | ||
* @param defaultValue - The value to be returned if the provided key is invalid. | ||
* @return The enum value for the provided key. | ||
* Returns `defaultValue` if the provided key is invalid. | ||
* Performs a strict lookup of enum value by key, with a default value | ||
* returned if the provided key is null/undefined. | ||
* This method is as strict as possible with compile-time types and run-time | ||
* validation for a lookup based on a (expected to be) valid key that is | ||
* guaranteed to return a valid value. | ||
* Throws an error if the provided key is invalid. | ||
* @param key - A valid key for this enum (or null/undefined). | ||
* @param defaultValue - A valid value to be returned if the key is null/undefined. | ||
* @return The value for the provided key, or the default value. | ||
* @throws {Error} if the provided key is invalid for this enum. | ||
*/ | ||
public getValueOrDefault( | ||
key: string | null | undefined, | ||
defaultValue?: T[StringKeyOf<T>] | ||
): T[StringKeyOf<T>] | undefined; | ||
public getValue<K extends StringKeyOf<E>>( | ||
key: K | null | undefined, | ||
// tslint:disable-next-line:unified-signatures | ||
defaultValue: undefined | ||
): E[K] | undefined; | ||
/** | ||
* Gets the enum value for the provided key. | ||
* Returns a default value if the provided key is invalid. | ||
* @param key - A potential key value for this enum. | ||
* @param defaultValue - The value to be returned if the provided key is invalid. | ||
* @return The enum value for the provided key. | ||
* Returns `defaultValue` if the provided key is invalid. | ||
* Performs a strict lookup of enum value by key, with a default value | ||
* returned if the provided key is null/undefined. | ||
* This method is as strict as possible with compile-time types and run-time | ||
* validation for a lookup based on a (expected to be) valid key that is | ||
* guaranteed to return a valid value. | ||
* Throws an error if the provided key is invalid. | ||
* @param key - A valid key for this enum (or null/undefined). | ||
* @param defaultValue - A valid value to be returned if the key is null/undefined. | ||
* @return The value for the provided key, or the default value. | ||
* @throws {Error} if the provided key is invalid for this enum. | ||
*/ | ||
public getValueOrDefault( | ||
key: string | null | undefined, | ||
defaultValue: V | ||
): V; | ||
public getValue<K extends StringKeyOf<E>, DefaultValue extends V>( | ||
key: K | null | undefined, | ||
defaultValue: DefaultValue & StrictEnumParam<V, DefaultValue> | ||
): E[K] | DefaultValue; | ||
/** | ||
* Gets the enum value for the provided key. | ||
* Returns a default value if the provided key is invalid. | ||
* @param key - A potential key value for this enum. | ||
* @param defaultValue - The value to be returned if the provided key is invalid. | ||
* @return The enum value for the provided key. | ||
* Returns `defaultValue` if the provided key is invalid. | ||
* Performs a strict lookup of enum value by key, with a default value | ||
* returned if the provided key is null/undefined. | ||
* This method is as strict as possible with compile-time types and run-time | ||
* validation for a lookup based on a (expected to be) valid key that is | ||
* guaranteed to return a valid value. | ||
* Throws an error if the provided key is invalid. | ||
* @param key - A valid key for this enum (or null/undefined). | ||
* @param defaultValue - A valid value to be returned if the key is null/undefined. | ||
* @return The value for the provided key, or the default value. | ||
* @throws {Error} if the provided key is invalid for this enum. | ||
*/ | ||
public getValueOrDefault( | ||
key: string | null | undefined, | ||
defaultValue: V | undefined | ||
): V | undefined; | ||
/** | ||
* Gets the enum value for the provided key. | ||
* Returns a default value if the provided key is invalid. | ||
* @param key - A potential key value for this enum. | ||
* @param defaultValue - The value to be returned if the provided key is invalid. | ||
* @return The enum value for the provided key. | ||
* Returns `defaultValue` if the provided key is invalid. | ||
*/ | ||
public getValueOrDefault( | ||
key: string | null | undefined, | ||
defaultValue?: T[StringKeyOf<T>] | V | ||
public getValue<K extends StringKeyOf<E>, DefaultValue extends V>( | ||
key: K | null | undefined, | ||
defaultValue?: DefaultValue & StrictEnumParam<V, DefaultValue> | ||
): E[K] | DefaultValue | undefined; | ||
public getValue( | ||
key: StringKeyOf<E> | null | undefined, | ||
defaultValue?: V | ||
): V | undefined { | ||
// NOTE: The key MUST be separately validated before looking up the entry in enumObj to avoid false positive | ||
// lookups for keys that match properties on Object.prototype, or keys that match the index keys of | ||
// reverse lookups on numeric enums. | ||
if (this.isKey(key)) { | ||
return this.enumObj[key]; | ||
const verifiedDefaultValue = | ||
defaultValue != null | ||
? this.asValueOrThrow(defaultValue) | ||
: undefined; | ||
if (key == null) { | ||
return verifiedDefaultValue; | ||
} | ||
const index = this.keysList.indexOf(key); | ||
if (index !== -1) { | ||
return this.valuesList[index]; | ||
} else { | ||
return defaultValue; | ||
throw new Error( | ||
`Unexpected key: ${key}. Expected one of: ${this.getKeys()}` | ||
); | ||
} | ||
@@ -721,11 +724,58 @@ } | ||
// HACK: Forcefully overriding the value of the [Symbol.toStringTag] property. | ||
// This was originally implemented in the class as recommended by MDN | ||
// Symbol.toStringTag documentation: | ||
// public get [Symbol.toStringTag](): string { return "EnumWrapper"; } | ||
// | ||
// However, after upgrading to TypeScript 3.7, this caused compiler errors | ||
// when running dtslint due to the getter being emitted to the .d.ts file, | ||
// but TSC complaining that getters aren't allowed in "ambient" contexts. | ||
// This seems to be realated to a known breaking change: | ||
// https://www.typescriptlang.org/docs/handbook/release-notes/typescript-3-7.html#class-field-mitigations | ||
// | ||
// To avoid requiring TypeScript 3.6+ to use ts-enum-util, I no longer | ||
// implement the getter on the class and instead simply set the value of | ||
// the [Symbol.toStringTag] property on the class prototype to the desired | ||
// string. | ||
// | ||
// I also tried implementing it as: | ||
// public readonly [Symbol.toStringTag] = "EnumWrapper"; | ||
// But this got emitted to the .d.ts file with the initializer, | ||
// causing a compiler time error about initializers not allowed in an | ||
// "ambient" context. So I had to omit the declaration of the | ||
// [Symbol.toStringTag] in the class declaration and hackishly set its | ||
// value here (not important to have it part of the class declaration | ||
// as long as the value exists at runtime). | ||
(EnumWrapper.prototype as any)[Symbol.toStringTag] = "EnumWrapper"; | ||
export namespace EnumWrapper { | ||
/** | ||
* A tuple containing the key and value of a single entry in an enum. | ||
* @template T - Type of an enum-like object. | ||
* | ||
* @template E - The type of the enum-like object that is wrapped by the EnumWrapper.. | ||
* @template V - The enum value type (always allow this to default!). | ||
* NOTE: This template param shouldn't need to exist. It is a workaround | ||
* to a TypeScript design limitation. | ||
* See: https://github.com/microsoft/TypeScript/issues/35322#issuecomment-558247434 | ||
*/ | ||
export type Entry< | ||
T extends Record<StringKeyOf<T>, number | string> = any | ||
> = Readonly<[StringKeyOf<T>, T[StringKeyOf<T>]]>; | ||
E extends Record<StringKeyOf<E>, V>, | ||
V extends number | string = E[StringKeyOf<E>] | ||
> = Readonly<[StringKeyOf<E>, V]>; | ||
export namespace Entry { | ||
/** | ||
* Helper type for defining a generalized EnumWrapper.Entry type for any kind | ||
* of enum whose values are assignable to type T. | ||
* | ||
* Example: | ||
* // Function that works with an EnumWrapper.Entry for any numeric enum | ||
* function foo(entry: EnumWrapper.Entry.OfType<number>): void; | ||
*/ | ||
// tslint:disable-next-line:no-shadowed-variable | ||
export type OfType<T extends number | string> = Entry< | ||
EnumWrapper.OfType<T> | ||
>; | ||
} | ||
/** | ||
@@ -740,112 +790,48 @@ * A function used in iterating all key/value entries in an enum. | ||
* @template R - The type of the result. | ||
* @template V - Type of the enum value. | ||
* @template T - Type of an enum-like object. | ||
* @template E - The type of the enum-like object that is wrapped by the EnumWrapper.. | ||
* @template V - The enum value type (always allow this to default!). | ||
* NOTE: This template param shouldn't need to exist. It is a workaround | ||
* to a TypeScript design limitation. | ||
* See: https://github.com/microsoft/TypeScript/issues/35322#issuecomment-558247434 | ||
*/ | ||
export type Iteratee< | ||
R = any, | ||
V extends number | string = number | string, | ||
T extends Record<StringKeyOf<T>, V> = any | ||
R, | ||
E extends Record<StringKeyOf<E>, V>, | ||
V extends number | string = E[StringKeyOf<E>] | ||
> = ( | ||
this: any, | ||
value: T[StringKeyOf<T>], | ||
key: StringKeyOf<T>, | ||
enumWrapper: EnumWrapper<V, T>, | ||
value: V, | ||
key: StringKeyOf<E>, | ||
enumWrapper: EnumWrapper<E, V>, | ||
index: number | ||
) => R; | ||
} | ||
/** | ||
* Type alias for an {@link EnumWrapper} for any type of enum-like object that contains only number values. | ||
* | ||
* @template T - Type of an enum-like object that contains only number values. | ||
*/ | ||
export type NumberEnumWrapper< | ||
T extends Record<StringKeyOf<T>, number> = any | ||
> = EnumWrapper<number, T>; | ||
export namespace Iteratee { | ||
/** | ||
* Helper type for defining a generalized EnumWrapper.Iteratee type that | ||
* returns type R for any kind of enum whose values are assignable to type T. | ||
* | ||
* Example: | ||
* // Function that works with an EnumWrapper.Iteratee for any numeric enum | ||
* // and returns a boolean value. | ||
* function foo(iteratee: EnumWrapper.Entry.OfType<boolean, number>): void; | ||
*/ | ||
// tslint:disable-next-line:no-shadowed-variable | ||
export type OfType< | ||
R, | ||
T extends number | string = number | string | ||
> = Iteratee<R, EnumWrapper.OfType<T>>; | ||
} | ||
export namespace NumberEnumWrapper { | ||
/** | ||
* Type alias for an {@link EnumWrapper.Entry} for any type of enum-like object that contains only number values. | ||
* Helper type for defining a generalized EnumWrapper type for any kind | ||
* of enum whose values are assignable to type T. | ||
* | ||
* @template T - Type of an enum-like object that contains only number values. | ||
* Example: | ||
* // Function that works with an EnumWrapper for any numeric enum | ||
* function foo(enumWrapper: EnumWrapper.OfType<number>): void; | ||
*/ | ||
export type Entry< | ||
T extends Record<StringKeyOf<T>, number> = any | ||
> = EnumWrapper.Entry<T>; | ||
/** | ||
* Type alias for an {@link EnumWrapper.Iteratee} for any type of enum-like object that contains only number values. | ||
* | ||
* @template R - The type of the result. | ||
* @template T - Type of an enum-like object that contains only number values. | ||
*/ | ||
export type Iteratee< | ||
R = any, | ||
T extends Record<StringKeyOf<T>, number> = any | ||
> = EnumWrapper.Iteratee<R, number, T>; | ||
export type OfType<T extends number | string> = EnumWrapper< | ||
Record<string, T> | ||
>; | ||
} | ||
/** | ||
* Type alias for an {@link EnumWrapper} for any type of enum-like object that contains only string values. | ||
* | ||
* @template T - Type of an enum-like object that contains only string values. | ||
*/ | ||
export type StringEnumWrapper< | ||
T extends Record<StringKeyOf<T>, string> = any | ||
> = EnumWrapper<string, T>; | ||
export namespace StringEnumWrapper { | ||
/** | ||
* Type alias for an {@link EnumWrapper.Entry} for any type of enum-like object that contains only string values. | ||
* | ||
* @template T - Type of an enum-like object that contains only string values. | ||
*/ | ||
export type Entry< | ||
T extends Record<StringKeyOf<T>, string> = any | ||
> = EnumWrapper.Entry<T>; | ||
/** | ||
* Type alias for an {@link EnumWrapper.Iteratee} for any type of enum-like object that contains only string values. | ||
* | ||
* @template R - The type of the result. | ||
* @template T - Type of an enum-like object that contains only string values. | ||
*/ | ||
export type Iteratee< | ||
R = any, | ||
T extends Record<StringKeyOf<T>, string> = any | ||
> = EnumWrapper.Iteratee<R, string, T>; | ||
} | ||
/** | ||
* Type alias for an {@link EnumWrapper} for any type of enum-like object that contains a mix of | ||
* number and string values. | ||
* | ||
* @template T - Type of an enum-like object that contains a mix of number and string values. | ||
*/ | ||
export type MixedEnumWrapper< | ||
T extends Record<StringKeyOf<T>, number | string> = any | ||
> = EnumWrapper<number | string, T>; | ||
export namespace MixedEnumWrapper { | ||
/** | ||
* Type alias for an {@link EnumWrapper.Entry} for any type of enum-like object that contains a mix of | ||
* number and string values. | ||
* | ||
* @template T - Type of an enum-like object that contains a mix of number and string values. | ||
*/ | ||
export type Entry< | ||
T extends Record<StringKeyOf<T>, number | string> = any | ||
> = EnumWrapper.Entry<T>; | ||
/** | ||
* Type alias for an {@link EnumWrapper.Iteratee} for any type of enum-like object that contains a mix of | ||
* number and string values. | ||
* | ||
* @template R - The type of the result. | ||
* @template T - Type of an enum-like object that contains a mix of number and string values. | ||
*/ | ||
export type Iteratee< | ||
R = any, | ||
T extends Record<StringKeyOf<T>, number | string> = any | ||
> = EnumWrapper.Iteratee<R, number | string, T>; | ||
} |
@@ -0,1 +1,2 @@ | ||
export * from "./EnumObject"; | ||
export * from "./EnumWrapper"; | ||
@@ -2,0 +3,0 @@ export * from "./EnumValueVisitor"; |
@@ -7,1 +7,9 @@ /** | ||
export type StringKeyOf<T> = Extract<keyof T, string>; | ||
/** | ||
* Extracts only keys of type T that are assignable to type `string` | ||
* and whose property values are asslignable to type V. | ||
*/ | ||
export type StringKeyOfType<T, V> = { | ||
[P in StringKeyOf<T>]: T[P] extends V ? P : never; | ||
}[StringKeyOf<T>]; |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
215581
11
101
3693
1
164