├── .prettierignore ├── src ├── .npmignore ├── types.ts ├── index.ts ├── createUnhandledEntryError.ts ├── symbols.ts ├── tsconfig.json ├── $enum.ts ├── objectKeysUtil.ts ├── EnumValueMapper.ts ├── mapEnumValue.ts ├── visitEnumValue.ts ├── EnumValueVisitor.ts ├── EnumValueVisitee.ts └── EnumValueMappee.ts ├── .prettierrc.json ├── .vscode └── settings.json ├── .gitignore ├── tests ├── README.md ├── $enum-number.test.ts ├── $enum-string.test.ts ├── $enum-mixed.test.ts ├── objectKeysUtil.test.ts └── mapValue-mixed.test.ts ├── type_tests ├── v2_9_plus │ ├── README.md │ ├── index.d.ts │ ├── tsconfig.json │ ├── mapValue │ │ ├── string-literal.dtslint.ts │ │ ├── string-literal-null.dtslint.ts │ │ ├── string-literal-undefined.dtslint.ts │ │ ├── string-enum.dtslint.ts │ │ ├── string-enum-null.dtslint.ts │ │ ├── string-enum-undefined.dtslint.ts │ │ ├── mixed-literal-both.dtslint.ts │ │ ├── string-literal-both.dtslint.ts │ │ ├── number-literal-both.dtslint.ts │ │ ├── number-enum-both.dtslint.ts │ │ ├── mixed-enum-both.dtslint.ts │ │ └── string-enum-both.dtslint.ts │ ├── visitValue │ │ ├── string-literal.dtslint.ts │ │ ├── string-enum.dtslint.ts │ │ ├── string-literal-null.dtslint.ts │ │ ├── string-literal-undefined.dtslint.ts │ │ ├── string-enum-null.dtslint.ts │ │ ├── string-enum-undefined.dtslint.ts │ │ ├── number-literal-both.dtslint.ts │ │ ├── mixed-literal-both.dtslint.ts │ │ ├── string-literal-both.dtslint.ts │ │ ├── number-enum-both.dtslint.ts │ │ ├── mixed-enum-both.dtslint.ts │ │ └── string-enum-both.dtslint.ts │ └── EnumWrapper │ │ ├── dictionary-number.dtslint.ts │ │ ├── dictionary-string.dtslint.ts │ │ ├── dictionary-mixed.dtslint.ts │ │ ├── object-number.dtslint.ts │ │ ├── object-string.dtslint.ts │ │ ├── object-mixed.dtslint.ts │ │ ├── enum-number.dtslint.ts │ │ ├── enum-string.dtslint.ts │ │ └── enum-mixed.dtslint.ts └── README.md ├── .travis.yml ├── tsconfig.json ├── LICENSE ├── tslint.json ├── package.json ├── docs └── migration_from_ts-string-visitor.md └── README.md /.prettierignore: -------------------------------------------------------------------------------- 1 | package.json 2 | -------------------------------------------------------------------------------- /src/.npmignore: -------------------------------------------------------------------------------- 1 | tsconfig.json 2 | .npmignore 3 | -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "tabWidth": 4, 3 | "arrowParens": "always" 4 | } 5 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "typescript.tsdk": "node_modules\\typescript\\lib" 3 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | jest_cache/ 3 | coverage/ 4 | dist/ 5 | ts-enum-util-*.tgz 6 | -------------------------------------------------------------------------------- /tests/README.md: -------------------------------------------------------------------------------- 1 | ## Run-Time Tests 2 | This folder contiains unit tests that verify run-time behavior of `ts-enum-util`. Unit tests are run via `jest`. -------------------------------------------------------------------------------- /type_tests/v2_9_plus/README.md: -------------------------------------------------------------------------------- 1 | ### Compile-Time Tests: TypeScript 2.9+ 2 | 3 | Proper support for defining types with numeric literal key types is only 4 | supported in TypeScript 2.9+ 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "10" 4 | script: 5 | - npm run build:travis 6 | cache: 7 | directories: 8 | - "node_modules" 9 | - "jest_cache" 10 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/index.d.ts: -------------------------------------------------------------------------------- 1 | // Dummy index.d.ts file for running dtslint. 2 | // We're not actually linting a .d.ts file; only using dtslint to verify compile time types of ts-enum-util. 3 | // TypeScript Version: 2.9 4 | -------------------------------------------------------------------------------- /src/types.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Extracts only keys of type T that are assignable to type `string`. 3 | * This is necessary starting with TypeScript 2.9 because keyof T can now 4 | * include `number` and `symbol` types. 5 | */ 6 | export type StringKeyOf = Extract; 7 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./EnumWrapper"; 2 | export * from "./EnumValueVisitor"; 3 | export * from "./EnumValueVisitee"; 4 | export * from "./EnumValueMapper"; 5 | export * from "./EnumValueMappee"; 6 | export * from "./$enum"; 7 | export * from "./mapEnumValue"; 8 | export * from "./visitEnumValue"; 9 | export * from "./symbols"; 10 | -------------------------------------------------------------------------------- /type_tests/README.md: -------------------------------------------------------------------------------- 1 | ## Compile-Time Tests 2 | This directory contains tests of compile-time types, using `dtslint`. Tests are broken down into subdirectories for different ranges of TypeScript versions. The code in these tests is not exectuted. It is only evaluated for compile-time type assertions. 3 | 4 | Each subdirectory is a complete independent environment for `dtslint`, each with its own `tsconfig.json`, `tslint.json`, and dummy `index.d.ts` files. -------------------------------------------------------------------------------- /src/createUnhandledEntryError.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Creates an Error with a message explaining that an unhandled 3 | * value was encountered. 4 | * @param unhandledValue - The unhandled value. 5 | * @return an Error with a message explaining that an unhandled 6 | * value was encountered. 7 | */ 8 | export function createUnhandledEntryError( 9 | unhandledValue: string | number | null | undefined 10 | ): Error { 11 | return new Error(`Unhandled value: ${unhandledValue}`); 12 | } 13 | -------------------------------------------------------------------------------- /tests/$enum-number.test.ts: -------------------------------------------------------------------------------- 1 | import { EnumWrapper, $enum } from "../src"; 2 | 3 | enum TestEnum { 4 | A, 5 | B, 6 | C 7 | } 8 | 9 | describe("$enum: number enum", () => { 10 | test("returns cached instance", () => { 11 | const result1 = $enum(TestEnum); 12 | const result2 = $enum(TestEnum); 13 | 14 | expect(result1 instanceof EnumWrapper).toBe(true); 15 | // returns cached instance 16 | expect(result1).toBe(result2); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /tests/$enum-string.test.ts: -------------------------------------------------------------------------------- 1 | import { EnumWrapper, $enum } from "../src"; 2 | 3 | enum TestEnum { 4 | A = "a", 5 | B = "b", 6 | C = "c" 7 | } 8 | 9 | describe("$enum: string enum", () => { 10 | test("returns cached instance", () => { 11 | const result1 = $enum(TestEnum); 12 | const result2 = $enum(TestEnum); 13 | 14 | expect(result1 instanceof EnumWrapper).toBe(true); 15 | // returns cached instance 16 | expect(result1).toBe(result2); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /tests/$enum-mixed.test.ts: -------------------------------------------------------------------------------- 1 | import { EnumWrapper, $enum } from "../src"; 2 | 3 | enum TestEnum { 4 | A, 5 | B = "2", 6 | C = 2 // duplicate of B, but number instead of string 7 | } 8 | 9 | describe("$enum: number+string enum", () => { 10 | test("returns cached instance", () => { 11 | const result1 = $enum(TestEnum); 12 | const result2 = $enum(TestEnum); 13 | 14 | expect(result1 instanceof EnumWrapper).toBe(true); 15 | // returns cached instance 16 | expect(result1).toBe(result2); 17 | }); 18 | }); 19 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* Config used for dtslint unit tests. */ 2 | { 3 | "compilerOptions": { 4 | /* Basic Options */ 5 | "target": "es5", 6 | "module": "commonjs", 7 | 8 | "lib": ["es6"], 9 | 10 | "downlevelIteration": true, 11 | 12 | /* Strict Type-Checking Options */ 13 | "strict": true, 14 | "alwaysStrict": true, 15 | 16 | /* Module Resolution Options */ 17 | "moduleResolution": "node", 18 | "baseUrl": "./", 19 | "paths": { 20 | "ts-enum-util": ["../../dist/types"] 21 | }, 22 | "types": [] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | /* Config used for generally compiling all code within the project (including unit tests, etc) to discover 2 | * compiler errors, but not for actually building the project distribution. 3 | * See tsconfig.json in the src/ directory for project distribution build config. */ 4 | { 5 | "compilerOptions": { 6 | /* Basic Options */ 7 | "target": "es5", 8 | "module": "commonjs", 9 | "lib": ["es6"], 10 | 11 | /* Strict Type-Checking Options */ 12 | "strict": true, 13 | "alwaysStrict": true, 14 | 15 | /* Module Resolution Options */ 16 | "moduleResolution": "node", 17 | "baseUrl": "./" 18 | }, 19 | "include": ["src", "tests"] 20 | } 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Jeff Lau 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["tslint:recommended", "tslint-config-prettier"], 3 | "rules": { 4 | "typedef": [ 5 | true, 6 | "call-signature", 7 | "parameter", 8 | "property-declaration" 9 | ], 10 | "new-parens": true, 11 | "no-var-requires": false, 12 | "no-arg": true, 13 | "no-conditional-assignment": true, 14 | "no-console": [ 15 | true, 16 | "debug", 17 | "info", 18 | "log", 19 | "time", 20 | "timeEnd", 21 | "trace" 22 | ], 23 | "array-type": [true, "array"], 24 | "ordered-imports": false, 25 | "class-name": false, 26 | "interface-name": false, 27 | "no-namespace": false, 28 | "no-default-export": true, 29 | "no-unused-variable": true, 30 | "object-literal-sort-keys": false, 31 | "object-literal-shorthand": false, 32 | "object-literal-key-quotes": false, 33 | "max-classes-per-file": [false], 34 | "triple-equals": [true, "allow-null-check"], 35 | "no-empty-interface": false, 36 | "no-string-literal": false, 37 | "jsdoc-format": true, 38 | "member-ordering": false, 39 | "forin": true, 40 | "no-for-in-array": true, 41 | "no-invalid-template-strings": true 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/symbols.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Unique symbol to be used as the value of an entry in a value 3 | * visitor/mapper implementation to indicate that the entry is explicitly 4 | * not handled. 5 | * If the corresponding value is encountered at run-time, then an error is 6 | * thrown. 7 | */ 8 | export const unhandledEntry = Symbol("ts-enum-util:unhandledEntry"); 9 | 10 | /** 11 | * Unique symbol used as a property name in value visitor/mapper 12 | * implementations to define a special null value handler. 13 | * A symbol is used to avoid any possibility of a collision with an actual 14 | * string enum value. 15 | */ 16 | export const handleNull = Symbol("ts-enum-util:handleNull"); 17 | 18 | /** 19 | * Unique symbol used as a property name in value visitor/mapper 20 | * implementations to define a special undefined value handler. 21 | * A symbol is used to avoid any possibility of a collision with an actual 22 | * string enum value. 23 | */ 24 | export const handleUndefined = Symbol("ts-enum-util:handleUndefined"); 25 | 26 | /** 27 | * Unique symbol used as a property name in value visitor/mapper 28 | * implementations to define a special "unexpected" value handler (handles 29 | * values at run-time that are unexpected based on compile time type). 30 | * A symbol is used to avoid any possibility of a collision with an actual 31 | * string enum value. 32 | */ 33 | export const handleUnexpected = Symbol("ts-enum-util:handleUnexpected"); 34 | -------------------------------------------------------------------------------- /src/tsconfig.json: -------------------------------------------------------------------------------- 1 | /* Config used for building the project distribution */ 2 | { 3 | "compilerOptions": { 4 | /* Basic Options */ 5 | "target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'. */ 6 | "module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */ 7 | "declaration": true, /* Generates corresponding '.d.ts' file. */ 8 | "sourceMap": true, 9 | "outDir": "../dist/commonjs", /* Redirect output structure to the directory. */ 10 | "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */ 11 | "lib": [ 12 | "es6" 13 | ], 14 | 15 | /* Strict Type-Checking Options */ 16 | "strict": true, /* Enable all strict type-checking options. */ 17 | "alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */ 18 | "noImplicitAny": true, 19 | 20 | /* Module Resolution Options */ 21 | "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ 22 | "baseUrl": "./" /* Base directory to resolve non-absolute module names. */ 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/mapValue/string-literal.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | type RGB = "r" | "g" | "b"; 4 | 5 | declare const rgb: RGB; 6 | 7 | // Return type is inferred 8 | // $ExpectType number 9 | $enum.mapValue(rgb).with({ 10 | r: 10, 11 | g: 20, 12 | b: 30 13 | }); 14 | // $ExpectType string 15 | $enum.mapValue(rgb).with({ 16 | r: "10", 17 | g: "20", 18 | b: "30" 19 | }); 20 | 21 | // Return type is inferred when "unhandled" entries exist 22 | // $ExpectType number 23 | $enum.mapValue(rgb).with({ 24 | r: 10, 25 | g: $enum.unhandledEntry, 26 | b: 30 27 | }); 28 | 29 | // handleUnexpected is allowed 30 | // $ExpectType number 31 | $enum.mapValue(rgb).with({ 32 | r: 10, 33 | g: 20, 34 | b: 30, 35 | [$enum.handleUnexpected]: -1 36 | }); 37 | 38 | // special handlers can be unhandled 39 | // $ExpectType number 40 | $enum.mapValue(rgb).with({ 41 | r: 10, 42 | g: 20, 43 | b: 30, 44 | [$enum.handleUnexpected]: $enum.unhandledEntry 45 | }); 46 | 47 | // Missing value handler causes error 48 | // $ExpectError 49 | $enum.mapValue(rgb).with({ 50 | r: 10, 51 | b: 30 52 | }); 53 | 54 | // Unexpected value handler causes error 55 | $enum.mapValue(rgb).with({ 56 | r: 10, 57 | // $ExpectError 58 | oops: 42, 59 | g: 20, 60 | b: 30 61 | }); 62 | 63 | // Unnecessary null handler causes error 64 | $enum.mapValue(rgb).with({ 65 | r: 10, 66 | g: 20, 67 | b: 30, 68 | // $ExpectError 69 | [$enum.handleNull]: -1 70 | }); 71 | 72 | // Unnecessary undefined handler causes error 73 | $enum.mapValue(rgb).with({ 74 | r: 10, 75 | g: 20, 76 | b: 30, 77 | // $ExpectError 78 | [$enum.handleUndefined]: -1 79 | }); 80 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/mapValue/string-literal-null.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | type RGB = "r" | "g" | "b"; 4 | 5 | declare const rgb: RGB | null; 6 | 7 | // Return type is inferred 8 | // $ExpectType number 9 | $enum.mapValue(rgb).with({ 10 | r: 10, 11 | g: 20, 12 | b: 30, 13 | [$enum.handleNull]: -1 14 | }); 15 | // $ExpectType string 16 | $enum.mapValue(rgb).with({ 17 | r: "10", 18 | g: "20", 19 | b: "30", 20 | [$enum.handleNull]: "-1" 21 | }); 22 | 23 | // Return type is inferred when "unhandled" entries exist 24 | // $ExpectType number 25 | $enum.mapValue(rgb).with({ 26 | r: 10, 27 | g: $enum.unhandledEntry, 28 | b: 30, 29 | [$enum.handleNull]: -1 30 | }); 31 | 32 | // handleUnexpected is allowed 33 | // $ExpectType number 34 | $enum.mapValue(rgb).with({ 35 | r: 10, 36 | g: 20, 37 | b: 30, 38 | [$enum.handleNull]: -1, 39 | [$enum.handleUnexpected]: -1 40 | }); 41 | 42 | // special handlers can be unhandled 43 | // $ExpectType number 44 | $enum.mapValue(rgb).with({ 45 | r: 10, 46 | g: 20, 47 | b: 30, 48 | [$enum.handleNull]: $enum.unhandledEntry, 49 | [$enum.handleUnexpected]: $enum.unhandledEntry 50 | }); 51 | 52 | // Missing value handler causes error 53 | // $ExpectError 54 | $enum.mapValue(rgb).with({ 55 | r: 10, 56 | b: 30, 57 | [$enum.handleNull]: -1 58 | }); 59 | 60 | // Unexpected value handler causes error 61 | $enum.mapValue(rgb).with({ 62 | r: 10, 63 | // $ExpectError 64 | oops: 42, 65 | g: 20, 66 | b: 30, 67 | [$enum.handleNull]: -1 68 | }); 69 | 70 | // missing null handler causes error 71 | // $ExpectError 72 | $enum.mapValue(rgb).with({ 73 | r: 10, 74 | g: 20, 75 | b: 30 76 | }); 77 | 78 | // Unnecessary undefined handler causes error 79 | $enum.mapValue(rgb).with({ 80 | r: 10, 81 | g: 20, 82 | b: 30, 83 | [$enum.handleNull]: -1, 84 | // $ExpectError 85 | [$enum.handleUndefined]: -1 86 | }); 87 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/mapValue/string-literal-undefined.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | type RGB = "r" | "g" | "b"; 4 | 5 | declare const rgb: RGB | undefined; 6 | 7 | // Return type is inferred 8 | // $ExpectType number 9 | $enum.mapValue(rgb).with({ 10 | r: 10, 11 | g: 20, 12 | b: 30, 13 | [$enum.handleUndefined]: -1 14 | }); 15 | // $ExpectType string 16 | $enum.mapValue(rgb).with({ 17 | r: "10", 18 | g: "20", 19 | b: "30", 20 | [$enum.handleUndefined]: "-1" 21 | }); 22 | 23 | // Return type is inferred when "unhandled" entries exist 24 | // $ExpectType number 25 | $enum.mapValue(rgb).with({ 26 | r: 10, 27 | g: $enum.unhandledEntry, 28 | b: 30, 29 | [$enum.handleUndefined]: -1 30 | }); 31 | 32 | // handleUnexpected is allowed 33 | // $ExpectType number 34 | $enum.mapValue(rgb).with({ 35 | r: 10, 36 | g: 20, 37 | b: 30, 38 | [$enum.handleUndefined]: -1, 39 | [$enum.handleUnexpected]: -1 40 | }); 41 | 42 | // special handlers can be unhandled 43 | // $ExpectType number 44 | $enum.mapValue(rgb).with({ 45 | r: 10, 46 | g: 20, 47 | b: 30, 48 | [$enum.handleUndefined]: $enum.unhandledEntry, 49 | [$enum.handleUnexpected]: $enum.unhandledEntry 50 | }); 51 | 52 | // Missing value handler causes error 53 | // $ExpectError 54 | $enum.mapValue(rgb).with({ 55 | r: 10, 56 | b: 30, 57 | [$enum.handleUndefined]: -1 58 | }); 59 | 60 | // Unexpected value handler causes error 61 | $enum.mapValue(rgb).with({ 62 | r: 10, 63 | // $ExpectError 64 | oops: 42, 65 | g: 20, 66 | b: 30, 67 | [$enum.handleUndefined]: -1 68 | }); 69 | 70 | // missing undefined handler causes error 71 | // $ExpectError 72 | $enum.mapValue(rgb).with({ 73 | r: 10, 74 | g: 20, 75 | b: 30 76 | }); 77 | 78 | // Unnecessary null handler causes error 79 | $enum.mapValue(rgb).with({ 80 | r: 10, 81 | g: 20, 82 | b: 30, 83 | // $ExpectError 84 | [$enum.handleNull]: -1, 85 | [$enum.handleUndefined]: -1 86 | }); 87 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/mapValue/string-enum.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | enum RGB { 4 | R = "r", 5 | G = "g", 6 | B = "b" 7 | } 8 | 9 | declare const rgb: RGB; 10 | 11 | // Return type is inferred 12 | // $ExpectType number 13 | $enum.mapValue(rgb).with({ 14 | [RGB.R]: 10, 15 | [RGB.G]: 20, 16 | [RGB.B]: 30 17 | }); 18 | // $ExpectType string 19 | $enum.mapValue(rgb).with({ 20 | [RGB.R]: "10", 21 | [RGB.G]: "20", 22 | [RGB.B]: "30" 23 | }); 24 | 25 | // Return type is inferred when "unhandled" entries exist 26 | // $ExpectType number 27 | $enum.mapValue(rgb).with({ 28 | [RGB.R]: 10, 29 | [RGB.G]: $enum.unhandledEntry, 30 | [RGB.B]: 30 31 | }); 32 | 33 | // handleUnexpected is allowed 34 | // $ExpectType number 35 | $enum.mapValue(rgb).with({ 36 | [RGB.R]: 10, 37 | [RGB.G]: 20, 38 | [RGB.B]: 30, 39 | [$enum.handleUnexpected]: -1 40 | }); 41 | 42 | // special handlers can be unhandled 43 | // $ExpectType number 44 | $enum.mapValue(rgb).with({ 45 | [RGB.R]: 10, 46 | [RGB.G]: 20, 47 | [RGB.B]: 30, 48 | [$enum.handleUnexpected]: $enum.unhandledEntry 49 | }); 50 | 51 | // Missing value handler causes error 52 | // $ExpectError 53 | $enum.mapValue(rgb).with({ 54 | [RGB.R]: 10, 55 | [RGB.B]: 30 56 | }); 57 | 58 | // Unexpected value handler causes error 59 | $enum.mapValue(rgb).with({ 60 | [RGB.R]: 10, 61 | // $ExpectError 62 | oops: 42, 63 | [RGB.G]: 20, 64 | [RGB.B]: 30 65 | }); 66 | 67 | // Unnecessary null handler causes error 68 | $enum.mapValue(rgb).with({ 69 | [RGB.R]: 10, 70 | [RGB.G]: 20, 71 | [RGB.B]: 30, 72 | // $ExpectError 73 | [$enum.handleNull]: -1 74 | }); 75 | 76 | // Unnecessary undefined handler causes error 77 | $enum.mapValue(rgb).with({ 78 | [RGB.R]: 10, 79 | [RGB.G]: 20, 80 | [RGB.B]: 30, 81 | // $ExpectError 82 | [$enum.handleUndefined]: -1 83 | }); 84 | 85 | // Test enum value computed property names (no compiler error). 86 | // (only supported as of TS 2.6.1) 87 | $enum.mapValue(rgb).with({ 88 | [RGB.R]: 10, 89 | [RGB.G]: 20, 90 | [RGB.B]: 30 91 | }); 92 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/mapValue/string-enum-null.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | enum RGB { 4 | R = "r", 5 | G = "g", 6 | B = "b" 7 | } 8 | 9 | declare const rgb: RGB | null; 10 | 11 | // Return type is inferred 12 | // $ExpectType number 13 | $enum.mapValue(rgb).with({ 14 | [RGB.R]: 10, 15 | [RGB.G]: 20, 16 | [RGB.B]: 30, 17 | [$enum.handleNull]: -1 18 | }); 19 | // $ExpectType string 20 | $enum.mapValue(rgb).with({ 21 | [RGB.R]: "10", 22 | [RGB.G]: "20", 23 | [RGB.B]: "30", 24 | [$enum.handleNull]: "-1" 25 | }); 26 | 27 | // Return type is inferred when "unhandled" entries exist 28 | // $ExpectType number 29 | $enum.mapValue(rgb).with({ 30 | [RGB.R]: 10, 31 | [RGB.G]: $enum.unhandledEntry, 32 | [RGB.B]: 30, 33 | [$enum.handleNull]: -1 34 | }); 35 | 36 | // handleUnexpected is allowed 37 | // $ExpectType number 38 | $enum.mapValue(rgb).with({ 39 | [RGB.R]: 10, 40 | [RGB.G]: 20, 41 | [RGB.B]: 30, 42 | [$enum.handleNull]: -1, 43 | [$enum.handleUnexpected]: -1 44 | }); 45 | 46 | // special handlers can be unhandled 47 | // $ExpectType number 48 | $enum.mapValue(rgb).with({ 49 | [RGB.R]: 10, 50 | [RGB.G]: 20, 51 | [RGB.B]: 30, 52 | [$enum.handleNull]: $enum.unhandledEntry, 53 | [$enum.handleUnexpected]: $enum.unhandledEntry 54 | }); 55 | 56 | // Missing value handler causes error 57 | // $ExpectError 58 | $enum.mapValue(rgb).with({ 59 | [RGB.R]: 10, 60 | [RGB.B]: 30, 61 | [$enum.handleNull]: -1 62 | }); 63 | 64 | // Unexpected value handler causes error 65 | $enum.mapValue(rgb).with({ 66 | [RGB.R]: 10, 67 | // $ExpectError 68 | oops: 42, 69 | [RGB.G]: 20, 70 | [RGB.B]: 30, 71 | [$enum.handleNull]: -1 72 | }); 73 | 74 | // missing null handler causes error 75 | // $ExpectError 76 | $enum.mapValue(rgb).with({ 77 | [RGB.R]: 10, 78 | [RGB.G]: 20, 79 | [RGB.B]: 30 80 | }); 81 | 82 | // Unnecessary undefined handler causes error 83 | $enum.mapValue(rgb).with({ 84 | [RGB.R]: 10, 85 | [RGB.G]: 20, 86 | [RGB.B]: 30, 87 | [$enum.handleNull]: -1, 88 | // $ExpectError 89 | [$enum.handleUndefined]: -1 90 | }); 91 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/mapValue/string-enum-undefined.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | enum RGB { 4 | R = "r", 5 | G = "g", 6 | B = "b" 7 | } 8 | 9 | declare const rgb: RGB | undefined; 10 | 11 | // Return type is inferred 12 | // $ExpectType number 13 | $enum.mapValue(rgb).with({ 14 | [RGB.R]: 10, 15 | [RGB.G]: 20, 16 | [RGB.B]: 30, 17 | [$enum.handleUndefined]: -1 18 | }); 19 | // $ExpectType string 20 | $enum.mapValue(rgb).with({ 21 | [RGB.R]: "10", 22 | [RGB.G]: "20", 23 | [RGB.B]: "30", 24 | [$enum.handleUndefined]: "-1" 25 | }); 26 | 27 | // Return type is inferred when "unhandled" entries exist 28 | // $ExpectType number 29 | $enum.mapValue(rgb).with({ 30 | [RGB.R]: 10, 31 | [RGB.G]: $enum.unhandledEntry, 32 | [RGB.B]: 30, 33 | [$enum.handleUndefined]: -1 34 | }); 35 | 36 | // handleUnexpected is allowed 37 | // $ExpectType number 38 | $enum.mapValue(rgb).with({ 39 | [RGB.R]: 10, 40 | [RGB.G]: 20, 41 | [RGB.B]: 30, 42 | [$enum.handleUndefined]: -1, 43 | [$enum.handleUnexpected]: -1 44 | }); 45 | 46 | // special handlers can be unhandled 47 | // $ExpectType number 48 | $enum.mapValue(rgb).with({ 49 | [RGB.R]: 10, 50 | [RGB.G]: 20, 51 | [RGB.B]: 30, 52 | [$enum.handleUndefined]: $enum.unhandledEntry, 53 | [$enum.handleUnexpected]: $enum.unhandledEntry 54 | }); 55 | 56 | // Missing value handler causes error 57 | // $ExpectError 58 | $enum.mapValue(rgb).with({ 59 | [RGB.R]: 10, 60 | [RGB.B]: 30, 61 | [$enum.handleUndefined]: -1 62 | }); 63 | 64 | // Unexpected value handler causes error 65 | $enum.mapValue(rgb).with({ 66 | [RGB.R]: 10, 67 | // $ExpectError 68 | oops: 42, 69 | [RGB.G]: 20, 70 | [RGB.B]: 30, 71 | [$enum.handleUndefined]: -1 72 | }); 73 | 74 | // missing undefined handler causes error 75 | // $ExpectError 76 | $enum.mapValue(rgb).with({ 77 | [RGB.R]: 10, 78 | [RGB.G]: 20, 79 | [RGB.B]: 30 80 | }); 81 | 82 | // Unnecessary null handler causes error 83 | $enum.mapValue(rgb).with({ 84 | [RGB.R]: 10, 85 | [RGB.G]: 20, 86 | [RGB.B]: 30, 87 | // $ExpectError 88 | [$enum.handleNull]: -1, 89 | [$enum.handleUndefined]: -1 90 | }); 91 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/mapValue/mixed-literal-both.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | type RGB = 1 | "g" | 3; 4 | 5 | declare const rgb: RGB | null | undefined; 6 | 7 | // Return type is inferred 8 | // $ExpectType number 9 | $enum.mapValue(rgb).with({ 10 | 1: 10, 11 | g: 20, 12 | 3: 30, 13 | [$enum.handleNull]: -1, 14 | [$enum.handleUndefined]: -1 15 | }); 16 | // $ExpectType string 17 | $enum.mapValue(rgb).with({ 18 | 1: "10", 19 | g: "20", 20 | 3: "30", 21 | [$enum.handleNull]: "-1", 22 | [$enum.handleUndefined]: "-1" 23 | }); 24 | 25 | // Return type is inferred when "unhandled" entries exist 26 | // $ExpectType number 27 | $enum.mapValue(rgb).with({ 28 | 1: 10, 29 | g: $enum.unhandledEntry, 30 | 3: 30, 31 | [$enum.handleNull]: -1, 32 | [$enum.handleUndefined]: -1 33 | }); 34 | 35 | // handleUnexpected is allowed 36 | // $ExpectType number 37 | $enum.mapValue(rgb).with({ 38 | 1: 10, 39 | g: 20, 40 | 3: 30, 41 | [$enum.handleNull]: -1, 42 | [$enum.handleUndefined]: -1, 43 | [$enum.handleUnexpected]: -1 44 | }); 45 | 46 | // special handlers can be unhandled 47 | // $ExpectType number 48 | $enum.mapValue(rgb).with({ 49 | 1: 10, 50 | g: 20, 51 | 3: 30, 52 | [$enum.handleNull]: $enum.unhandledEntry, 53 | [$enum.handleUndefined]: $enum.unhandledEntry, 54 | [$enum.handleUnexpected]: $enum.unhandledEntry 55 | }); 56 | 57 | // Missing value handler causes error 58 | // $ExpectError 59 | $enum.mapValue(rgb).with({ 60 | 1: 10, 61 | 3: 30, 62 | [$enum.handleNull]: -1, 63 | [$enum.handleUndefined]: -1 64 | }); 65 | 66 | // Unexpected value handler causes error 67 | $enum.mapValue(rgb).with({ 68 | 1: 10, 69 | // $ExpectError 70 | oops: 42, 71 | g: 20, 72 | 3: 30, 73 | [$enum.handleNull]: -1, 74 | [$enum.handleUndefined]: -1 75 | }); 76 | 77 | // missing null handler causes error 78 | // $ExpectError 79 | $enum.mapValue(rgb).with({ 80 | 1: 10, 81 | g: 20, 82 | 3: 30, 83 | [$enum.handleUndefined]: -1 84 | }); 85 | 86 | // missing undefined handler causes error 87 | // $ExpectError 88 | $enum.mapValue(rgb).with({ 89 | 1: 10, 90 | g: 20, 91 | 3: 30, 92 | [$enum.handleNull]: -1 93 | }); 94 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/mapValue/string-literal-both.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | type RGB = "r" | "g" | "b"; 4 | 5 | declare const rgb: RGB | null | undefined; 6 | 7 | // Return type is inferred 8 | // $ExpectType number 9 | $enum.mapValue(rgb).with({ 10 | r: 10, 11 | g: 20, 12 | b: 30, 13 | [$enum.handleNull]: -1, 14 | [$enum.handleUndefined]: -1 15 | }); 16 | // $ExpectType string 17 | $enum.mapValue(rgb).with({ 18 | r: "10", 19 | g: "20", 20 | b: "30", 21 | [$enum.handleNull]: "-1", 22 | [$enum.handleUndefined]: "-1" 23 | }); 24 | 25 | // Return type is inferred when "unhandled" entries exist 26 | // $ExpectType number 27 | $enum.mapValue(rgb).with({ 28 | r: 10, 29 | g: $enum.unhandledEntry, 30 | b: 30, 31 | [$enum.handleNull]: -1, 32 | [$enum.handleUndefined]: -1 33 | }); 34 | 35 | // handleUnexpected is allowed 36 | // $ExpectType number 37 | $enum.mapValue(rgb).with({ 38 | r: 10, 39 | g: 20, 40 | b: 30, 41 | [$enum.handleNull]: -1, 42 | [$enum.handleUndefined]: -1, 43 | [$enum.handleUnexpected]: -1 44 | }); 45 | 46 | // special handlers can be unhandled 47 | // $ExpectType number 48 | $enum.mapValue(rgb).with({ 49 | r: 10, 50 | g: 20, 51 | b: 30, 52 | [$enum.handleNull]: $enum.unhandledEntry, 53 | [$enum.handleUndefined]: $enum.unhandledEntry, 54 | [$enum.handleUnexpected]: $enum.unhandledEntry 55 | }); 56 | 57 | // Missing value handler causes error 58 | // $ExpectError 59 | $enum.mapValue(rgb).with({ 60 | r: 10, 61 | b: 30, 62 | [$enum.handleNull]: -1, 63 | [$enum.handleUndefined]: -1 64 | }); 65 | 66 | // Unexpected value handler causes error 67 | $enum.mapValue(rgb).with({ 68 | r: 10, 69 | // $ExpectError 70 | oops: 42, 71 | g: 20, 72 | b: 30, 73 | [$enum.handleNull]: -1, 74 | [$enum.handleUndefined]: -1 75 | }); 76 | 77 | // missing null handler causes error 78 | // $ExpectError 79 | $enum.mapValue(rgb).with({ 80 | r: 10, 81 | g: 20, 82 | b: 30, 83 | [$enum.handleUndefined]: -1 84 | }); 85 | 86 | // missing undefined handler causes error 87 | // $ExpectError 88 | $enum.mapValue(rgb).with({ 89 | r: 10, 90 | g: 20, 91 | b: 30, 92 | [$enum.handleNull]: -1 93 | }); 94 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/mapValue/number-literal-both.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | type RGB = 1 | 2 | 3; 4 | 5 | declare const rgb: RGB | null | undefined; 6 | 7 | // Return type is inferred 8 | // $ExpectType number 9 | $enum.mapValue(rgb).with({ 10 | 1: 10, 11 | 2: 20, 12 | 3: 30, 13 | [$enum.handleNull]: -1, 14 | [$enum.handleUndefined]: -1 15 | }); 16 | // $ExpectType string 17 | $enum.mapValue(rgb).with({ 18 | 1: "10", 19 | 2: "20", 20 | 3: "30", 21 | [$enum.handleNull]: "-1", 22 | [$enum.handleUndefined]: "-1" 23 | }); 24 | 25 | // Return type is inferred when "unhandled" entries exist 26 | // TODO: report error in @next to TypeScript. Re-enable test if it is fixed. 27 | // ExpectType number 28 | // $enum.mapValue(rgb).with({ 29 | // 1: 10, 30 | // 2: $enum.unhandledEntry, 31 | // 3: 30, 32 | // [$enum.handleNull]: -1, 33 | // [$enum.handleUndefined]: -1 34 | // }); 35 | 36 | // handleUnexpected is allowed 37 | // $ExpectType number 38 | $enum.mapValue(rgb).with({ 39 | 1: 10, 40 | 2: 20, 41 | 3: 30, 42 | [$enum.handleNull]: -1, 43 | [$enum.handleUndefined]: -1, 44 | [$enum.handleUnexpected]: -1 45 | }); 46 | 47 | // special handlers can be unhandled 48 | // $ExpectType number 49 | $enum.mapValue(rgb).with({ 50 | 1: 10, 51 | 2: 20, 52 | 3: 30, 53 | [$enum.handleNull]: $enum.unhandledEntry, 54 | [$enum.handleUndefined]: $enum.unhandledEntry, 55 | [$enum.handleUnexpected]: $enum.unhandledEntry 56 | }); 57 | 58 | // Missing value handler causes error 59 | // $ExpectError 60 | $enum.mapValue(rgb).with({ 61 | 1: 10, 62 | 3: 30, 63 | [$enum.handleNull]: -1, 64 | [$enum.handleUndefined]: -1 65 | }); 66 | 67 | // Unexpected value handler causes error 68 | $enum.mapValue(rgb).with({ 69 | 1: 10, 70 | // $ExpectError 71 | oops: 42, 72 | 2: 20, 73 | 3: 30, 74 | [$enum.handleNull]: -1, 75 | [$enum.handleUndefined]: -1 76 | }); 77 | 78 | // missing null handler causes error 79 | // $ExpectError 80 | $enum.mapValue(rgb).with({ 81 | 1: 10, 82 | 2: 20, 83 | 3: 30, 84 | [$enum.handleUndefined]: -1 85 | }); 86 | 87 | // missing undefined handler causes error 88 | // $ExpectError 89 | $enum.mapValue(rgb).with({ 90 | 1: 10, 91 | 2: 20, 92 | 3: 30, 93 | [$enum.handleNull]: -1 94 | }); 95 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/mapValue/number-enum-both.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | enum RGB { 4 | R, 5 | G, 6 | B 7 | } 8 | 9 | declare const rgb: RGB | null | undefined; 10 | 11 | // Return type is inferred 12 | // $ExpectType number 13 | $enum.mapValue(rgb).with({ 14 | [RGB.R]: 10, 15 | [RGB.G]: 20, 16 | [RGB.B]: 30, 17 | [$enum.handleNull]: -1, 18 | [$enum.handleUndefined]: -1 19 | }); 20 | // $ExpectType string 21 | $enum.mapValue(rgb).with({ 22 | [RGB.R]: "10", 23 | [RGB.G]: "20", 24 | [RGB.B]: "30", 25 | [$enum.handleNull]: "-1", 26 | [$enum.handleUndefined]: "-1" 27 | }); 28 | 29 | // Return type is inferred when "unhandled" entries exist 30 | // $ExpectType number 31 | $enum.mapValue(rgb).with({ 32 | [RGB.R]: 10, 33 | [RGB.G]: $enum.unhandledEntry, 34 | [RGB.B]: 30, 35 | [$enum.handleNull]: -1, 36 | [$enum.handleUndefined]: -1 37 | }); 38 | 39 | // handleUnexpected is allowed 40 | // $ExpectType number 41 | $enum.mapValue(rgb).with({ 42 | [RGB.R]: 10, 43 | [RGB.G]: 20, 44 | [RGB.B]: 30, 45 | [$enum.handleNull]: -1, 46 | [$enum.handleUndefined]: -1, 47 | [$enum.handleUnexpected]: -1 48 | }); 49 | 50 | // special handlers can be unhandled 51 | // $ExpectType number 52 | $enum.mapValue(rgb).with({ 53 | [RGB.R]: 10, 54 | [RGB.G]: 20, 55 | [RGB.B]: 30, 56 | [$enum.handleNull]: $enum.unhandledEntry, 57 | [$enum.handleUndefined]: $enum.unhandledEntry, 58 | [$enum.handleUnexpected]: $enum.unhandledEntry 59 | }); 60 | 61 | // Missing value handler causes error 62 | // $ExpectError 63 | $enum.mapValue(rgb).with({ 64 | [RGB.R]: 10, 65 | [RGB.B]: 30, 66 | [$enum.handleNull]: -1, 67 | [$enum.handleUndefined]: -1 68 | }); 69 | 70 | // Unexpected value handler causes error 71 | $enum.mapValue(rgb).with({ 72 | [RGB.R]: 10, 73 | // $ExpectError 74 | oops: 42, 75 | [RGB.G]: 20, 76 | [RGB.B]: 30, 77 | [$enum.handleNull]: -1, 78 | [$enum.handleUndefined]: -1 79 | }); 80 | 81 | // missing null handler causes error 82 | // $ExpectError 83 | $enum.mapValue(rgb).with({ 84 | [RGB.R]: 10, 85 | [RGB.G]: 20, 86 | [RGB.B]: 30, 87 | [$enum.handleUndefined]: -1 88 | }); 89 | 90 | // missing undefined handler causes error 91 | // $ExpectError 92 | $enum.mapValue(rgb).with({ 93 | [RGB.R]: 10, 94 | [RGB.G]: 20, 95 | [RGB.B]: 30, 96 | [$enum.handleNull]: -1 97 | }); 98 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/mapValue/mixed-enum-both.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | enum RGB { 4 | R = 1, 5 | G = "G", 6 | B = 3 7 | } 8 | 9 | declare const rgb: RGB | null | undefined; 10 | 11 | // Return type is inferred 12 | // $ExpectType number 13 | $enum.mapValue(rgb).with({ 14 | [RGB.R]: 10, 15 | [RGB.G]: 20, 16 | [RGB.B]: 30, 17 | [$enum.handleNull]: -1, 18 | [$enum.handleUndefined]: -1 19 | }); 20 | // $ExpectType string 21 | $enum.mapValue(rgb).with({ 22 | [RGB.R]: "10", 23 | [RGB.G]: "20", 24 | [RGB.B]: "30", 25 | [$enum.handleNull]: "-1", 26 | [$enum.handleUndefined]: "-1" 27 | }); 28 | 29 | // Return type is inferred when "unhandled" entries exist 30 | // $ExpectType number 31 | $enum.mapValue(rgb).with({ 32 | [RGB.R]: 10, 33 | [RGB.G]: $enum.unhandledEntry, 34 | [RGB.B]: 30, 35 | [$enum.handleNull]: -1, 36 | [$enum.handleUndefined]: -1 37 | }); 38 | 39 | // handleUnexpected is allowed 40 | // $ExpectType number 41 | $enum.mapValue(rgb).with({ 42 | [RGB.R]: 10, 43 | [RGB.G]: 20, 44 | [RGB.B]: 30, 45 | [$enum.handleNull]: -1, 46 | [$enum.handleUndefined]: -1, 47 | [$enum.handleUnexpected]: -1 48 | }); 49 | 50 | // special handlers can be unhandled 51 | // $ExpectType number 52 | $enum.mapValue(rgb).with({ 53 | [RGB.R]: 10, 54 | [RGB.G]: 20, 55 | [RGB.B]: 30, 56 | [$enum.handleNull]: $enum.unhandledEntry, 57 | [$enum.handleUndefined]: $enum.unhandledEntry, 58 | [$enum.handleUnexpected]: $enum.unhandledEntry 59 | }); 60 | 61 | // Missing value handler causes error 62 | // $ExpectError 63 | $enum.mapValue(rgb).with({ 64 | [RGB.R]: 10, 65 | [RGB.B]: 30, 66 | [$enum.handleNull]: -1, 67 | [$enum.handleUndefined]: -1 68 | }); 69 | 70 | // Unexpected value handler causes error 71 | $enum.mapValue(rgb).with({ 72 | [RGB.R]: 10, 73 | // $ExpectError 74 | oops: 42, 75 | [RGB.G]: 20, 76 | [RGB.B]: 30, 77 | [$enum.handleNull]: -1, 78 | [$enum.handleUndefined]: -1 79 | }); 80 | 81 | // missing null handler causes error 82 | // $ExpectError 83 | $enum.mapValue(rgb).with({ 84 | [RGB.R]: 10, 85 | [RGB.G]: 20, 86 | [RGB.B]: 30, 87 | [$enum.handleUndefined]: -1 88 | }); 89 | 90 | // missing undefined handler causes error 91 | // $ExpectError 92 | $enum.mapValue(rgb).with({ 93 | [RGB.R]: 10, 94 | [RGB.G]: 20, 95 | [RGB.B]: 30, 96 | [$enum.handleNull]: -1 97 | }); 98 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/mapValue/string-enum-both.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | enum RGB { 4 | R = "r", 5 | G = "g", 6 | B = "b" 7 | } 8 | 9 | declare const rgb: RGB | null | undefined; 10 | 11 | // Return type is inferred 12 | // $ExpectType number 13 | $enum.mapValue(rgb).with({ 14 | [RGB.R]: 10, 15 | [RGB.G]: 20, 16 | [RGB.B]: 30, 17 | [$enum.handleNull]: -1, 18 | [$enum.handleUndefined]: -1 19 | }); 20 | // $ExpectType string 21 | $enum.mapValue(rgb).with({ 22 | [RGB.R]: "10", 23 | [RGB.G]: "20", 24 | [RGB.B]: "30", 25 | [$enum.handleNull]: "-1", 26 | [$enum.handleUndefined]: "-1" 27 | }); 28 | 29 | // Return type is inferred when "unhandled" entries exist 30 | // $ExpectType number 31 | $enum.mapValue(rgb).with({ 32 | [RGB.R]: 10, 33 | [RGB.G]: $enum.unhandledEntry, 34 | [RGB.B]: 30, 35 | [$enum.handleNull]: -1, 36 | [$enum.handleUndefined]: -1 37 | }); 38 | 39 | // handleUnexpected is allowed 40 | // $ExpectType number 41 | $enum.mapValue(rgb).with({ 42 | [RGB.R]: 10, 43 | [RGB.G]: 20, 44 | [RGB.B]: 30, 45 | [$enum.handleNull]: -1, 46 | [$enum.handleUndefined]: -1, 47 | [$enum.handleUnexpected]: -1 48 | }); 49 | 50 | // special handlers can be unhandled 51 | // $ExpectType number 52 | $enum.mapValue(rgb).with({ 53 | [RGB.R]: 10, 54 | [RGB.G]: 20, 55 | [RGB.B]: 30, 56 | [$enum.handleNull]: $enum.unhandledEntry, 57 | [$enum.handleUndefined]: $enum.unhandledEntry, 58 | [$enum.handleUnexpected]: $enum.unhandledEntry 59 | }); 60 | 61 | // Missing value handler causes error 62 | // $ExpectError 63 | $enum.mapValue(rgb).with({ 64 | [RGB.R]: 10, 65 | [RGB.B]: 30, 66 | [$enum.handleNull]: -1, 67 | [$enum.handleUndefined]: -1 68 | }); 69 | 70 | // Unexpected value handler causes error 71 | $enum.mapValue(rgb).with({ 72 | [RGB.R]: 10, 73 | // $ExpectError 74 | oops: 42, 75 | [RGB.G]: 20, 76 | [RGB.B]: 30, 77 | [$enum.handleNull]: -1, 78 | [$enum.handleUndefined]: -1 79 | }); 80 | 81 | // missing null handler causes error 82 | // $ExpectError 83 | $enum.mapValue(rgb).with({ 84 | [RGB.R]: 10, 85 | [RGB.G]: 20, 86 | [RGB.B]: 30, 87 | [$enum.handleUndefined]: -1 88 | }); 89 | 90 | // missing undefined handler causes error 91 | // $ExpectError 92 | $enum.mapValue(rgb).with({ 93 | [RGB.R]: 10, 94 | [RGB.G]: 20, 95 | [RGB.B]: 30, 96 | [$enum.handleNull]: -1 97 | }); 98 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/visitValue/string-literal.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | type RGB = "r" | "g" | "b"; 4 | 5 | declare const rgb: RGB; 6 | 7 | // Test param types 8 | $enum.visitValue(rgb).with({ 9 | r: (value) => { 10 | // $ExpectType "r" 11 | value; 12 | }, 13 | g: (value) => { 14 | // $ExpectType "g" 15 | value; 16 | }, 17 | b: (value) => { 18 | // $ExpectType "b" 19 | value; 20 | }, 21 | [$enum.handleUnexpected]: (value) => { 22 | // $ExpectType string | null | undefined 23 | value; 24 | } 25 | }); 26 | 27 | // handleUnexpected is optional 28 | $enum.visitValue(rgb).with({ 29 | r: (value) => {}, 30 | g: (value) => {}, 31 | b: (value) => {} 32 | }); 33 | 34 | // Return type is inferred 35 | // $ExpectType number 36 | $enum.visitValue(rgb).with({ 37 | r: (value) => 10, 38 | g: (value) => 20, 39 | b: (value) => 30 40 | }); 41 | // $ExpectType string 42 | $enum.visitValue(rgb).with({ 43 | r: (value) => "10", 44 | g: (value) => "20", 45 | b: (value) => "30" 46 | }); 47 | 48 | // Return type is inferred when "unhandled" entries exist 49 | // $ExpectType number 50 | $enum.visitValue(rgb).with({ 51 | r: (value) => 10, 52 | g: $enum.unhandledEntry, 53 | b: (value) => 30 54 | }); 55 | 56 | // special handlers can be unhandled 57 | // $ExpectType number 58 | $enum.visitValue(rgb).with({ 59 | r: (value) => 10, 60 | g: (value) => 20, 61 | b: (value) => 30, 62 | [$enum.handleUnexpected]: $enum.unhandledEntry 63 | }); 64 | 65 | // Missing value handler causes error 66 | // $ExpectError 67 | $enum.visitValue(rgb).with({ 68 | r: (value) => {}, 69 | b: (value) => {} 70 | }); 71 | 72 | // Unexpected value handler causes error 73 | $enum.visitValue(rgb).with({ 74 | r: (value) => {}, 75 | // $ExpectError 76 | oops: (value) => {}, 77 | g: (value) => {}, 78 | b: (value) => {} 79 | }); 80 | 81 | // Unnecessary null handler causes error 82 | $enum.visitValue(rgb).with({ 83 | r: (value) => {}, 84 | g: (value) => {}, 85 | b: (value) => {}, 86 | // $ExpectError 87 | [$enum.handleNull]: (value) => {} 88 | }); 89 | 90 | // Unnecessary undefined handler causes error 91 | $enum.visitValue(rgb).with({ 92 | r: (value) => {}, 93 | g: (value) => {}, 94 | b: (value) => {}, 95 | // $ExpectError 96 | [$enum.handleUndefined]: (value) => {} 97 | }); 98 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/visitValue/string-enum.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | enum RGB { 4 | R = "r", 5 | G = "g", 6 | B = "b" 7 | } 8 | 9 | declare const rgb: RGB; 10 | 11 | // Test param types 12 | $enum.visitValue(rgb).with({ 13 | [RGB.R]: (value) => { 14 | // $ExpectType RGB.R 15 | value; 16 | }, 17 | [RGB.G]: (value) => { 18 | // $ExpectType RGB.G 19 | value; 20 | }, 21 | [RGB.B]: (value) => { 22 | // $ExpectType RGB.B 23 | value; 24 | }, 25 | [$enum.handleUnexpected]: (value) => { 26 | // $ExpectType string | null | undefined 27 | value; 28 | } 29 | }); 30 | 31 | // handleUnexpected is optional 32 | $enum.visitValue(rgb).with({ 33 | [RGB.R]: (value) => {}, 34 | [RGB.G]: (value) => {}, 35 | [RGB.B]: (value) => {} 36 | }); 37 | 38 | // Return type is inferred 39 | // $ExpectType number 40 | $enum.visitValue(rgb).with({ 41 | [RGB.R]: (value) => 10, 42 | [RGB.G]: (value) => 20, 43 | [RGB.B]: (value) => 30 44 | }); 45 | // $ExpectType string 46 | $enum.visitValue(rgb).with({ 47 | [RGB.R]: (value) => "10", 48 | [RGB.G]: (value) => "20", 49 | [RGB.B]: (value) => "30" 50 | }); 51 | 52 | // Return type is inferred when "unhandled" entries exist 53 | // $ExpectType number 54 | $enum.visitValue(rgb).with({ 55 | [RGB.R]: (value) => 10, 56 | [RGB.G]: $enum.unhandledEntry, 57 | [RGB.B]: (value) => 30 58 | }); 59 | 60 | // special handlers can be unhandled 61 | // $ExpectType number 62 | $enum.visitValue(rgb).with({ 63 | [RGB.R]: (value) => 10, 64 | [RGB.G]: (value) => 20, 65 | [RGB.B]: (value) => 30, 66 | [$enum.handleUnexpected]: $enum.unhandledEntry 67 | }); 68 | 69 | // Missing value handler causes error 70 | // $ExpectError 71 | $enum.visitValue(rgb).with({ 72 | [RGB.R]: (value) => {}, 73 | [RGB.B]: (value) => {} 74 | }); 75 | 76 | // Unexpected value handler causes error 77 | $enum.visitValue(rgb).with({ 78 | [RGB.R]: (value) => {}, 79 | // $ExpectError 80 | oops: (value) => {}, 81 | [RGB.G]: (value) => {}, 82 | [RGB.B]: (value) => {} 83 | }); 84 | 85 | // Unnecessary null handler causes error 86 | $enum.visitValue(rgb).with({ 87 | [RGB.R]: (value) => {}, 88 | [RGB.G]: (value) => {}, 89 | [RGB.B]: (value) => {}, 90 | // $ExpectError 91 | [$enum.handleNull]: (value) => {} 92 | }); 93 | 94 | // Unnecessary undefined handler causes error 95 | $enum.visitValue(rgb).with({ 96 | [RGB.R]: (value) => {}, 97 | [RGB.G]: (value) => {}, 98 | [RGB.B]: (value) => {}, 99 | // $ExpectError 100 | [$enum.handleUndefined]: (value) => {} 101 | }); 102 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/visitValue/string-literal-null.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | type RGB = "r" | "g" | "b"; 4 | 5 | declare const rgb: RGB | null; 6 | 7 | // Test param types 8 | $enum.visitValue(rgb).with({ 9 | r: (value) => { 10 | // $ExpectType "r" 11 | value; 12 | }, 13 | g: (value) => { 14 | // $ExpectType "g" 15 | value; 16 | }, 17 | b: (value) => { 18 | // $ExpectType "b" 19 | value; 20 | }, 21 | [$enum.handleNull]: (value) => { 22 | // $ExpectType null 23 | value; 24 | }, 25 | [$enum.handleUnexpected]: (value) => { 26 | // $ExpectType string | undefined 27 | value; 28 | } 29 | }); 30 | 31 | // handleUnexpected is optional 32 | $enum.visitValue(rgb).with({ 33 | r: (value) => {}, 34 | g: (value) => {}, 35 | b: (value) => {}, 36 | [$enum.handleNull]: (value) => {} 37 | }); 38 | 39 | // Return type is inferred 40 | // $ExpectType number 41 | $enum.visitValue(rgb).with({ 42 | r: (value) => 10, 43 | g: (value) => 20, 44 | b: (value) => 30, 45 | [$enum.handleNull]: (value) => -1 46 | }); 47 | // $ExpectType string 48 | $enum.visitValue(rgb).with({ 49 | r: (value) => "10", 50 | g: (value) => "20", 51 | b: (value) => "30", 52 | [$enum.handleNull]: (value) => "-1" 53 | }); 54 | 55 | // Return type is inferred when "unhandled" entries exist 56 | // $ExpectType number 57 | $enum.visitValue(rgb).with({ 58 | r: (value) => 10, 59 | g: $enum.unhandledEntry, 60 | b: (value) => 30, 61 | [$enum.handleNull]: (value) => -1 62 | }); 63 | 64 | // special handlers can be unhandled 65 | // $ExpectType number 66 | $enum.visitValue(rgb).with({ 67 | r: (value) => 10, 68 | g: (value) => 20, 69 | b: (value) => 30, 70 | [$enum.handleNull]: $enum.unhandledEntry, 71 | [$enum.handleUnexpected]: $enum.unhandledEntry 72 | }); 73 | 74 | // Missing value handler causes error 75 | // $ExpectError 76 | $enum.visitValue(rgb).with({ 77 | r: (value) => {}, 78 | b: (value) => {}, 79 | [$enum.handleNull]: (value) => {} 80 | }); 81 | 82 | // Missing null handler causes error 83 | // $ExpectError 84 | $enum.visitValue(rgb).with({ 85 | r: (value) => {}, 86 | g: (value) => {}, 87 | b: (value) => {} 88 | }); 89 | 90 | // Unexpected value handler causes error 91 | $enum.visitValue(rgb).with({ 92 | r: (value) => {}, 93 | // $ExpectError 94 | oops: (value) => {}, 95 | g: (value) => {}, 96 | b: (value) => {}, 97 | [$enum.handleNull]: (value) => {} 98 | }); 99 | 100 | // Unnecessary undefined handler causes error 101 | $enum.visitValue(rgb).with({ 102 | r: (value) => {}, 103 | g: (value) => {}, 104 | b: (value) => {}, 105 | [$enum.handleNull]: (value) => {}, 106 | // $ExpectError 107 | [$enum.handleUndefined]: (value) => {} 108 | }); 109 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/visitValue/string-literal-undefined.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | type RGB = "r" | "g" | "b"; 4 | 5 | declare const rgb: RGB | undefined; 6 | 7 | // Test param types 8 | $enum.visitValue(rgb).with({ 9 | r: (value) => { 10 | // $ExpectType "r" 11 | value; 12 | }, 13 | g: (value) => { 14 | // $ExpectType "g" 15 | value; 16 | }, 17 | b: (value) => { 18 | // $ExpectType "b" 19 | value; 20 | }, 21 | [$enum.handleUndefined]: (value) => { 22 | // $ExpectType undefined 23 | value; 24 | }, 25 | [$enum.handleUnexpected]: (value) => { 26 | // $ExpectType string | null 27 | value; 28 | } 29 | }); 30 | 31 | // handleUnexpected is optional 32 | $enum.visitValue(rgb).with({ 33 | r: (value) => {}, 34 | g: (value) => {}, 35 | b: (value) => {}, 36 | [$enum.handleUndefined]: (value) => {} 37 | }); 38 | 39 | // Return type is inferred 40 | // $ExpectType number 41 | $enum.visitValue(rgb).with({ 42 | r: (value) => 10, 43 | g: (value) => 20, 44 | b: (value) => 30, 45 | [$enum.handleUndefined]: (value) => -1 46 | }); 47 | // $ExpectType string 48 | $enum.visitValue(rgb).with({ 49 | r: (value) => "10", 50 | g: (value) => "20", 51 | b: (value) => "30", 52 | [$enum.handleUndefined]: (value) => "-1" 53 | }); 54 | 55 | // Return type is inferred when "unhandled" entries exist 56 | // $ExpectType number 57 | $enum.visitValue(rgb).with({ 58 | r: (value) => 10, 59 | g: $enum.unhandledEntry, 60 | b: (value) => 30, 61 | [$enum.handleUndefined]: (value) => -1 62 | }); 63 | 64 | // special handlers can be unhandled 65 | // $ExpectType number 66 | $enum.visitValue(rgb).with({ 67 | r: (value) => 10, 68 | g: (value) => 20, 69 | b: (value) => 30, 70 | [$enum.handleUndefined]: $enum.unhandledEntry, 71 | [$enum.handleUnexpected]: $enum.unhandledEntry 72 | }); 73 | 74 | // Missing value handler causes error 75 | // $ExpectError 76 | $enum.visitValue(rgb).with({ 77 | r: (value) => {}, 78 | b: (value) => {}, 79 | [$enum.handleUndefined]: (value) => {} 80 | }); 81 | 82 | // Missing undefined handler causes error 83 | // $ExpectError 84 | $enum.visitValue(rgb).with({ 85 | r: (value) => {}, 86 | g: (value) => {}, 87 | b: (value) => {} 88 | }); 89 | 90 | // Unexpected value handler causes error 91 | $enum.visitValue(rgb).with({ 92 | r: (value) => {}, 93 | // $ExpectError 94 | oops: (value) => {}, 95 | g: (value) => {}, 96 | b: (value) => {}, 97 | [$enum.handleUndefined]: (value) => {} 98 | }); 99 | 100 | // Unnecessary null handler causes error 101 | $enum.visitValue(rgb).with({ 102 | r: (value) => {}, 103 | g: (value) => {}, 104 | b: (value) => {}, 105 | // $ExpectError 106 | [$enum.handleNull]: (value) => {}, 107 | [$enum.handleUndefined]: (value) => {} 108 | }); 109 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/visitValue/string-enum-null.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | enum RGB { 4 | R = "r", 5 | G = "g", 6 | B = "b" 7 | } 8 | 9 | declare const rgb: RGB | null; 10 | 11 | // Test param types 12 | $enum.visitValue(rgb).with({ 13 | [RGB.R]: (value) => { 14 | // $ExpectType RGB.R 15 | value; 16 | }, 17 | [RGB.G]: (value) => { 18 | // $ExpectType RGB.G 19 | value; 20 | }, 21 | [RGB.B]: (value) => { 22 | // $ExpectType RGB.B 23 | value; 24 | }, 25 | [$enum.handleNull]: (value) => { 26 | // $ExpectType null 27 | value; 28 | }, 29 | [$enum.handleUnexpected]: (value) => { 30 | // $ExpectType string | undefined 31 | value; 32 | } 33 | }); 34 | 35 | // handleUnexpected is optional 36 | $enum.visitValue(rgb).with({ 37 | [RGB.R]: (value) => {}, 38 | [RGB.G]: (value) => {}, 39 | [RGB.B]: (value) => {}, 40 | [$enum.handleNull]: (value) => {} 41 | }); 42 | 43 | // Return type is inferred 44 | // $ExpectType number 45 | $enum.visitValue(rgb).with({ 46 | [RGB.R]: (value) => 10, 47 | [RGB.G]: (value) => 20, 48 | [RGB.B]: (value) => 30, 49 | [$enum.handleNull]: (value) => -1 50 | }); 51 | // $ExpectType string 52 | $enum.visitValue(rgb).with({ 53 | [RGB.R]: (value) => "10", 54 | [RGB.G]: (value) => "20", 55 | [RGB.B]: (value) => "30", 56 | [$enum.handleNull]: (value) => "-1" 57 | }); 58 | 59 | // Return type is inferred when "unhandled" entries exist 60 | // $ExpectType number 61 | $enum.visitValue(rgb).with({ 62 | [RGB.R]: (value) => 10, 63 | [RGB.G]: $enum.unhandledEntry, 64 | [RGB.B]: (value) => 30, 65 | [$enum.handleNull]: (value) => -1 66 | }); 67 | 68 | // special handlers can be unhandled 69 | // $ExpectType number 70 | $enum.visitValue(rgb).with({ 71 | [RGB.R]: (value) => 10, 72 | [RGB.G]: (value) => 20, 73 | [RGB.B]: (value) => 30, 74 | [$enum.handleNull]: $enum.unhandledEntry, 75 | [$enum.handleUnexpected]: $enum.unhandledEntry 76 | }); 77 | 78 | // Missing value handler causes error 79 | // $ExpectError 80 | $enum.visitValue(rgb).with({ 81 | [RGB.R]: (value) => {}, 82 | [RGB.B]: (value) => {}, 83 | [$enum.handleNull]: (value) => {} 84 | }); 85 | 86 | // Missing null handler causes error 87 | // $ExpectError 88 | $enum.visitValue(rgb).with({ 89 | [RGB.R]: (value) => {}, 90 | [RGB.G]: (value) => {}, 91 | [RGB.B]: (value) => {} 92 | }); 93 | 94 | // Unexpected value handler causes error 95 | $enum.visitValue(rgb).with({ 96 | [RGB.R]: (value) => {}, 97 | // $ExpectError 98 | oops: (value) => {}, 99 | [RGB.G]: (value) => {}, 100 | [RGB.B]: (value) => {}, 101 | [$enum.handleNull]: (value) => {} 102 | }); 103 | 104 | // Unnecessary undefined handler causes error 105 | $enum.visitValue(rgb).with({ 106 | [RGB.R]: (value) => {}, 107 | [RGB.G]: (value) => {}, 108 | [RGB.B]: (value) => {}, 109 | [$enum.handleNull]: (value) => {}, 110 | // $ExpectError 111 | [$enum.handleUndefined]: (value) => {} 112 | }); 113 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/visitValue/string-enum-undefined.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | enum RGB { 4 | R = "r", 5 | G = "g", 6 | B = "b" 7 | } 8 | 9 | declare const rgb: RGB | undefined; 10 | 11 | // Test param types 12 | $enum.visitValue(rgb).with({ 13 | [RGB.R]: (value) => { 14 | // $ExpectType RGB.R 15 | value; 16 | }, 17 | [RGB.G]: (value) => { 18 | // $ExpectType RGB.G 19 | value; 20 | }, 21 | [RGB.B]: (value) => { 22 | // $ExpectType RGB.B 23 | value; 24 | }, 25 | [$enum.handleUndefined]: (value) => { 26 | // $ExpectType undefined 27 | value; 28 | }, 29 | [$enum.handleUnexpected]: (value) => { 30 | // $ExpectType string | null 31 | value; 32 | } 33 | }); 34 | 35 | // handleUnexpected is optional 36 | $enum.visitValue(rgb).with({ 37 | [RGB.R]: (value) => {}, 38 | [RGB.G]: (value) => {}, 39 | [RGB.B]: (value) => {}, 40 | [$enum.handleUndefined]: (value) => {} 41 | }); 42 | 43 | // Return type is inferred 44 | // $ExpectType number 45 | $enum.visitValue(rgb).with({ 46 | [RGB.R]: (value) => 10, 47 | [RGB.G]: (value) => 20, 48 | [RGB.B]: (value) => 30, 49 | [$enum.handleUndefined]: (value) => -1 50 | }); 51 | // $ExpectType string 52 | $enum.visitValue(rgb).with({ 53 | [RGB.R]: (value) => "10", 54 | [RGB.G]: (value) => "20", 55 | [RGB.B]: (value) => "30", 56 | [$enum.handleUndefined]: (value) => "-1" 57 | }); 58 | 59 | // Return type is inferred when "unhandled" entries exist 60 | // $ExpectType number 61 | $enum.visitValue(rgb).with({ 62 | [RGB.R]: (value) => 10, 63 | [RGB.G]: $enum.unhandledEntry, 64 | [RGB.B]: (value) => 30, 65 | [$enum.handleUndefined]: (value) => -1 66 | }); 67 | 68 | // special handlers can be unhandled 69 | // $ExpectType number 70 | $enum.visitValue(rgb).with({ 71 | [RGB.R]: (value) => 10, 72 | [RGB.G]: (value) => 20, 73 | [RGB.B]: (value) => 30, 74 | [$enum.handleUndefined]: $enum.unhandledEntry, 75 | [$enum.handleUnexpected]: $enum.unhandledEntry 76 | }); 77 | 78 | // Missing value handler causes error 79 | // $ExpectError 80 | $enum.visitValue(rgb).with({ 81 | [RGB.R]: (value) => {}, 82 | [RGB.B]: (value) => {}, 83 | [$enum.handleUndefined]: (value) => {} 84 | }); 85 | 86 | // Missing undefined handler causes error 87 | // $ExpectError 88 | $enum.visitValue(rgb).with({ 89 | [RGB.R]: (value) => {}, 90 | [RGB.G]: (value) => {}, 91 | [RGB.B]: (value) => {} 92 | }); 93 | 94 | // Unexpected value handler causes error 95 | $enum.visitValue(rgb).with({ 96 | [RGB.R]: (value) => {}, 97 | // $ExpectError 98 | oops: (value) => {}, 99 | [RGB.G]: (value) => {}, 100 | [RGB.B]: (value) => {}, 101 | [$enum.handleUndefined]: (value) => {} 102 | }); 103 | 104 | // Unnecessary null handler causes error 105 | $enum.visitValue(rgb).with({ 106 | [RGB.R]: (value) => {}, 107 | [RGB.G]: (value) => {}, 108 | [RGB.B]: (value) => {}, 109 | // $ExpectError 110 | [$enum.handleNull]: (value) => {}, 111 | [$enum.handleUndefined]: (value) => {} 112 | }); 113 | -------------------------------------------------------------------------------- /src/$enum.ts: -------------------------------------------------------------------------------- 1 | import { EnumWrapper } from "./EnumWrapper"; 2 | import { StringKeyOf } from "./types"; 3 | import * as symbols from "./symbols"; 4 | import { visitEnumValue } from "./visitEnumValue"; 5 | import { mapEnumValue } from "./mapEnumValue"; 6 | 7 | /** 8 | * Map of enum object -> EnumWrapper instance. 9 | * Used as a cache for {@link $enum}. 10 | * NOTE: WeakMap has very fast lookups and avoids memory leaks if used on a 11 | * temporary enum-like object. Even if a WeakMap implementation is very 12 | * naiive (like a Map polyfill), lookups are plenty fast for this use case 13 | * of a relatively small number of enums within a project. Just don't 14 | * perform cached lookups inside tight loops when you could cache the 15 | * result in a local variable, and you'll be fine :) 16 | * {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap} 17 | * {@link https://www.measurethat.net/Benchmarks/Show/2513/5/map-keyed-by-object} 18 | */ 19 | const enumWrapperInstancesCache = new WeakMap(); 20 | 21 | /** 22 | * Gets a cached EnumWrapper for an enum-like object with number values. 23 | * Creates and caches a new EnumWrapper if one is not already cached. 24 | * @param enumObj - An enum-like object with number values. 25 | * @return An instance of EnumWrapper for the provided enumObj. 26 | * 27 | * @template T - Type of the enum-like object that is being wrapped. 28 | */ 29 | export function $enum< 30 | V extends number, 31 | T extends Record, number> 32 | >(enumObj: T): EnumWrapper; 33 | /** 34 | * Gets a cached EnumWrapper for an enum-like object with string values. 35 | * Creates and caches a new EnumWrapper if one is not already cached. 36 | * @param enumObj - An enum-like object with string values. 37 | * @return An instance of EnumWrapper for the provided enumObj. 38 | * 39 | * @template T - Type of the enum-like object that is being wrapped. 40 | */ 41 | export function $enum, string>>( 42 | enumObj: T 43 | ): EnumWrapper; 44 | /** 45 | * Gets a cached EnumWrapper for an enum-like object with a mixture of number 46 | * and string values. 47 | * Creates and caches a new EnumWrapper if one is not already cached. 48 | * @param enumObj - An enum-like object with a mixture of number and string 49 | * values. 50 | * @return An instance of EnumWrapper for the provided enumObj. 51 | * 52 | * @template T - Type of the enum-like object that is being wrapped. 53 | */ 54 | export function $enum, number | string>>( 55 | enumObj: T 56 | ): EnumWrapper; 57 | export function $enum(enumObj: object): EnumWrapper { 58 | let result = enumWrapperInstancesCache.get(enumObj); 59 | 60 | if (!result) { 61 | result = new EnumWrapper(enumObj); 62 | enumWrapperInstancesCache.set(enumObj, result); 63 | } 64 | 65 | return result; 66 | } 67 | 68 | $enum.handleNull = symbols.handleNull; 69 | $enum.handleUndefined = symbols.handleUndefined; 70 | $enum.handleUnexpected = symbols.handleUnexpected; 71 | $enum.unhandledEntry = symbols.unhandledEntry; 72 | $enum.visitValue = visitEnumValue; 73 | $enum.mapValue = mapEnumValue; 74 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/visitValue/number-literal-both.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | type RGB = 1 | 2 | 3; 4 | 5 | declare const rgb: RGB | null | undefined; 6 | 7 | // Test param types 8 | $enum.visitValue(rgb).with({ 9 | 1: (value) => { 10 | // $ExpectType 1 11 | value; 12 | }, 13 | 2: (value) => { 14 | // $ExpectType 2 15 | value; 16 | }, 17 | 3: (value) => { 18 | // $ExpectType 3 19 | value; 20 | }, 21 | [$enum.handleNull]: (value) => { 22 | // $ExpectType null 23 | value; 24 | }, 25 | [$enum.handleUndefined]: (value) => { 26 | // $ExpectType undefined 27 | value; 28 | }, 29 | [$enum.handleUnexpected]: (value) => { 30 | // $ExpectType number 31 | value; 32 | } 33 | }); 34 | 35 | // handleUnexpected is optional 36 | $enum.visitValue(rgb).with({ 37 | 1: (value) => {}, 38 | 2: (value) => {}, 39 | 3: (value) => {}, 40 | [$enum.handleNull]: (value) => {}, 41 | [$enum.handleUndefined]: (value) => {} 42 | }); 43 | 44 | // Return type is inferred 45 | // $ExpectType number 46 | $enum.visitValue(rgb).with({ 47 | 1: (value) => 10, 48 | 2: (value) => 20, 49 | 3: (value) => 30, 50 | [$enum.handleNull]: (value) => -1, 51 | [$enum.handleUndefined]: (value) => -1 52 | }); 53 | // $ExpectType string 54 | $enum.visitValue(rgb).with({ 55 | 1: (value) => "10", 56 | 2: (value) => "20", 57 | 3: (value) => "30", 58 | [$enum.handleNull]: (value) => "-1", 59 | [$enum.handleUndefined]: (value) => "-1" 60 | }); 61 | 62 | // Return type is inferred when "unhandled" entries exist 63 | // $ExpectType number 64 | $enum.visitValue(rgb).with({ 65 | 1: (value) => 10, 66 | 2: $enum.unhandledEntry, 67 | 3: (value) => 30, 68 | [$enum.handleNull]: (value) => -1, 69 | [$enum.handleUndefined]: (value) => -1 70 | }); 71 | 72 | // special handlers can be unhandled 73 | // $ExpectType number 74 | $enum.visitValue(rgb).with({ 75 | 1: (value) => 10, 76 | 2: (value) => 20, 77 | 3: (value) => 30, 78 | [$enum.handleNull]: $enum.unhandledEntry, 79 | [$enum.handleUndefined]: $enum.unhandledEntry, 80 | [$enum.handleUnexpected]: $enum.unhandledEntry 81 | }); 82 | 83 | // Missing value handler causes error 84 | // $ExpectError 85 | $enum.visitValue(rgb).with({ 86 | 1: (value) => {}, 87 | 3: (value) => {}, 88 | [$enum.handleNull]: (value) => {}, 89 | [$enum.handleUndefined]: (value) => {} 90 | }); 91 | 92 | // Missing null handler causes error 93 | // $ExpectError 94 | $enum.visitValue(rgb).with({ 95 | 1: (value) => {}, 96 | 2: (value) => {}, 97 | 3: (value) => {}, 98 | [$enum.handleUndefined]: (value) => {} 99 | }); 100 | 101 | // Missing undefined handler causes error 102 | // $ExpectError 103 | $enum.visitValue(rgb).with({ 104 | 1: (value) => {}, 105 | 2: (value) => {}, 106 | 3: (value) => {}, 107 | [$enum.handleNull]: (value) => {} 108 | }); 109 | 110 | // Unexpected value handler causes error 111 | $enum.visitValue(rgb).with({ 112 | 1: (value) => {}, 113 | // $ExpectError 114 | oops: (value) => {}, 115 | 2: (value) => {}, 116 | 3: (value) => {}, 117 | [$enum.handleNull]: (value) => {}, 118 | [$enum.handleUndefined]: (value) => {} 119 | }); 120 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/visitValue/mixed-literal-both.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | type RGB = 1 | "g" | 3; 4 | 5 | declare const rgb: RGB | null | undefined; 6 | 7 | // Test param types 8 | $enum.visitValue(rgb).with({ 9 | 1: (value) => { 10 | // $ExpectType 1 11 | value; 12 | }, 13 | g: (value) => { 14 | // $ExpectType "g" 15 | value; 16 | }, 17 | 3: (value) => { 18 | // $ExpectType 3 19 | value; 20 | }, 21 | [$enum.handleNull]: (value) => { 22 | // $ExpectType null 23 | value; 24 | }, 25 | [$enum.handleUndefined]: (value) => { 26 | // $ExpectType undefined 27 | value; 28 | }, 29 | [$enum.handleUnexpected]: (value) => { 30 | // $ExpectType string | number 31 | value; 32 | } 33 | }); 34 | 35 | // handleUnexpected is optional 36 | $enum.visitValue(rgb).with({ 37 | 1: (value) => {}, 38 | g: (value) => {}, 39 | 3: (value) => {}, 40 | [$enum.handleNull]: (value) => {}, 41 | [$enum.handleUndefined]: (value) => {} 42 | }); 43 | 44 | // Return type is inferred 45 | // $ExpectType number 46 | $enum.visitValue(rgb).with({ 47 | 1: (value) => 10, 48 | g: (value) => 20, 49 | 3: (value) => 30, 50 | [$enum.handleNull]: (value) => -1, 51 | [$enum.handleUndefined]: (value) => -1 52 | }); 53 | // $ExpectType string 54 | $enum.visitValue(rgb).with({ 55 | 1: (value) => "10", 56 | g: (value) => "20", 57 | 3: (value) => "30", 58 | [$enum.handleNull]: (value) => "-1", 59 | [$enum.handleUndefined]: (value) => "-1" 60 | }); 61 | 62 | // Return type is inferred when "unhandled" entries exist 63 | // $ExpectType number 64 | $enum.visitValue(rgb).with({ 65 | 1: (value) => 10, 66 | g: $enum.unhandledEntry, 67 | 3: (value) => 30, 68 | [$enum.handleNull]: (value) => -1, 69 | [$enum.handleUndefined]: (value) => -1 70 | }); 71 | 72 | // special handlers can be unhandled 73 | // $ExpectType number 74 | $enum.visitValue(rgb).with({ 75 | 1: (value) => 10, 76 | g: (value) => 20, 77 | 3: (value) => 30, 78 | [$enum.handleNull]: $enum.unhandledEntry, 79 | [$enum.handleUndefined]: $enum.unhandledEntry, 80 | [$enum.handleUnexpected]: $enum.unhandledEntry 81 | }); 82 | 83 | // Missing value handler causes error 84 | // $ExpectError 85 | $enum.visitValue(rgb).with({ 86 | 1: (value) => {}, 87 | 3: (value) => {}, 88 | [$enum.handleNull]: (value) => {}, 89 | [$enum.handleUndefined]: (value) => {} 90 | }); 91 | 92 | // Missing null handler causes error 93 | // $ExpectError 94 | $enum.visitValue(rgb).with({ 95 | 1: (value) => {}, 96 | g: (value) => {}, 97 | 3: (value) => {}, 98 | [$enum.handleUndefined]: (value) => {} 99 | }); 100 | 101 | // Missing undefined handler causes error 102 | // $ExpectError 103 | $enum.visitValue(rgb).with({ 104 | 1: (value) => {}, 105 | g: (value) => {}, 106 | 3: (value) => {}, 107 | [$enum.handleNull]: (value) => {} 108 | }); 109 | 110 | // Unexpected value handler causes error 111 | $enum.visitValue(rgb).with({ 112 | 1: (value) => {}, 113 | // $ExpectError 114 | oops: (value) => {}, 115 | g: (value) => {}, 116 | 3: (value) => {}, 117 | [$enum.handleNull]: (value) => {}, 118 | [$enum.handleUndefined]: (value) => {} 119 | }); 120 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/visitValue/string-literal-both.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | type RGB = "r" | "g" | "b"; 4 | 5 | declare const rgb: RGB | null | undefined; 6 | 7 | // Test param types 8 | $enum.visitValue(rgb).with({ 9 | r: (value) => { 10 | // $ExpectType "r" 11 | value; 12 | }, 13 | g: (value) => { 14 | // $ExpectType "g" 15 | value; 16 | }, 17 | b: (value) => { 18 | // $ExpectType "b" 19 | value; 20 | }, 21 | [$enum.handleNull]: (value) => { 22 | // $ExpectType null 23 | value; 24 | }, 25 | [$enum.handleUndefined]: (value) => { 26 | // $ExpectType undefined 27 | value; 28 | }, 29 | [$enum.handleUnexpected]: (value) => { 30 | // $ExpectType string 31 | value; 32 | } 33 | }); 34 | 35 | // handleUnexpected is optional 36 | $enum.visitValue(rgb).with({ 37 | r: (value) => {}, 38 | g: (value) => {}, 39 | b: (value) => {}, 40 | [$enum.handleNull]: (value) => {}, 41 | [$enum.handleUndefined]: (value) => {} 42 | }); 43 | 44 | // Return type is inferred 45 | // $ExpectType number 46 | $enum.visitValue(rgb).with({ 47 | r: (value) => 10, 48 | g: (value) => 20, 49 | b: (value) => 30, 50 | [$enum.handleNull]: (value) => -1, 51 | [$enum.handleUndefined]: (value) => -1 52 | }); 53 | // $ExpectType string 54 | $enum.visitValue(rgb).with({ 55 | r: (value) => "10", 56 | g: (value) => "20", 57 | b: (value) => "30", 58 | [$enum.handleNull]: (value) => "-1", 59 | [$enum.handleUndefined]: (value) => "-1" 60 | }); 61 | 62 | // Return type is inferred when "unhandled" entries exist 63 | // $ExpectType number 64 | $enum.visitValue(rgb).with({ 65 | r: (value) => 10, 66 | g: $enum.unhandledEntry, 67 | b: (value) => 30, 68 | [$enum.handleNull]: (value) => -1, 69 | [$enum.handleUndefined]: (value) => -1 70 | }); 71 | 72 | // special handlers can be unhandled 73 | // $ExpectType number 74 | $enum.visitValue(rgb).with({ 75 | r: (value) => 10, 76 | g: (value) => 20, 77 | b: (value) => 30, 78 | [$enum.handleNull]: $enum.unhandledEntry, 79 | [$enum.handleUndefined]: $enum.unhandledEntry, 80 | [$enum.handleUnexpected]: $enum.unhandledEntry 81 | }); 82 | 83 | // Missing value handler causes error 84 | // $ExpectError 85 | $enum.visitValue(rgb).with({ 86 | r: (value) => {}, 87 | b: (value) => {}, 88 | [$enum.handleNull]: (value) => {}, 89 | [$enum.handleUndefined]: (value) => {} 90 | }); 91 | 92 | // Missing null handler causes error 93 | // $ExpectError 94 | $enum.visitValue(rgb).with({ 95 | r: (value) => {}, 96 | g: (value) => {}, 97 | b: (value) => {}, 98 | [$enum.handleUndefined]: (value) => {} 99 | }); 100 | 101 | // Missing undefined handler causes error 102 | // $ExpectError 103 | $enum.visitValue(rgb).with({ 104 | r: (value) => {}, 105 | g: (value) => {}, 106 | b: (value) => {}, 107 | [$enum.handleNull]: (value) => {} 108 | }); 109 | 110 | // Unexpected value handler causes error 111 | $enum.visitValue(rgb).with({ 112 | r: (value) => {}, 113 | // $ExpectError 114 | oops: (value) => {}, 115 | g: (value) => {}, 116 | b: (value) => {}, 117 | [$enum.handleNull]: (value) => {}, 118 | [$enum.handleUndefined]: (value) => {} 119 | }); 120 | -------------------------------------------------------------------------------- /tests/objectKeysUtil.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | isNonNumericKey, 3 | getOwnEnumerableNonNumericKeysES6, 4 | getOwnEnumerableNonNumericKeysES5, 5 | getOwnEnumerableNonNumericKeysES3 6 | } from "../src/objectKeysUtil"; 7 | 8 | describe("objectKeysUtil", () => { 9 | describe("isNonNumericKey", () => { 10 | test("Integers are numeric keys", () => { 11 | expect(isNonNumericKey("0")).toBe(false); 12 | expect(isNonNumericKey("1")).toBe(false); 13 | expect(isNonNumericKey("42")).toBe(false); 14 | // 2^32-2 15 | expect(isNonNumericKey("4294967294")).toBe(false); 16 | }); 17 | 18 | test("Integers >= 2^32-1 are numeric keys", () => { 19 | // 2^32-1 20 | expect(isNonNumericKey("4294967295")).toBe(false); 21 | // 2^32 22 | expect(isNonNumericKey("4294967296")).toBe(false); 23 | }); 24 | 25 | test("Negative integers are numeric keys", () => { 26 | expect(isNonNumericKey("-1")).toBe(false); 27 | expect(isNonNumericKey("-42")).toBe(false); 28 | }); 29 | 30 | test("Floating point numbers are numeric keys", () => { 31 | expect(isNonNumericKey("3.14")).toBe(false); 32 | expect(isNonNumericKey("-1.7")).toBe(false); 33 | }); 34 | 35 | test("Integers with extra formatting are NOT numeric keys", () => { 36 | expect(isNonNumericKey("01")).toBe(true); 37 | expect(isNonNumericKey(" 1")).toBe(true); 38 | expect(isNonNumericKey("1 ")).toBe(true); 39 | expect(isNonNumericKey("1,000")).toBe(true); 40 | }); 41 | 42 | test("Floating point numbers with extra formatting are NOT numeric keys", () => { 43 | expect(isNonNumericKey("1.000")).toBe(true); 44 | expect(isNonNumericKey("01.2")).toBe(true); 45 | expect(isNonNumericKey(" 1.2")).toBe(true); 46 | expect(isNonNumericKey("1.2 ")).toBe(true); 47 | expect(isNonNumericKey("1,000.2")).toBe(true); 48 | }); 49 | 50 | test("Non-numeric keys are NOT numeric keys", () => { 51 | expect(isNonNumericKey("1A")).toBe(true); 52 | expect(isNonNumericKey("Hello!")).toBe(true); 53 | expect(isNonNumericKey(" ")).toBe(true); 54 | expect(isNonNumericKey("")).toBe(true); 55 | }); 56 | }); 57 | 58 | describe("getOwnEnumerableNonNumericKeys", () => { 59 | const obj = ["a", "b", "c"]; 60 | 61 | Object.defineProperty(obj, "B", { 62 | value: 4, 63 | enumerable: true 64 | }); 65 | 66 | Object.defineProperty(obj, "A", { 67 | value: 3, 68 | enumerable: true 69 | }); 70 | 71 | Object.defineProperty(obj, "D", { 72 | value: 2, 73 | enumerable: false 74 | }); 75 | 76 | Object.defineProperty(obj, "C", { 77 | value: 1, 78 | enumerable: true 79 | }); 80 | 81 | Object.defineProperty(obj, "1.2", { 82 | value: 42, 83 | enumerable: true 84 | }); 85 | 86 | const expected = ["B", "A", "C"]; 87 | 88 | test.each([ 89 | ["ES6", getOwnEnumerableNonNumericKeysES6], 90 | ["ES5", getOwnEnumerableNonNumericKeysES5], 91 | ["ES3", getOwnEnumerableNonNumericKeysES3] 92 | ] as const)("getOwnEnumerableNonNumericKeys%s", (label, func) => { 93 | expect(func(obj)).toEqual(expected); 94 | }); 95 | }); 96 | }); 97 | -------------------------------------------------------------------------------- /src/objectKeysUtil.ts: -------------------------------------------------------------------------------- 1 | import { StringKeyOf } from "./types"; 2 | 3 | /** 4 | * Return true if the specified object key value is NOT a numeric key. 5 | * @param key - An object key. 6 | * @return true if the specified object key value is NOT a numeric key. 7 | */ 8 | export function isNonNumericKey(key: string): boolean { 9 | return key !== String(parseFloat(key)); 10 | } 11 | 12 | /** 13 | * Get all own enumerable string (non-numeric) keys of an object. 14 | * Implemented in terms of methods available in ES6. 15 | * The order of the result is *guaranteed* to be in the same order in which the 16 | * properties were added to the object, due to the specification of the 17 | * Object.getOwnPropertyNames method. 18 | * (first all numeric keys) 19 | * @param obj - An object. 20 | * @return A list of all the object's own enumerable string (non-numeric) keys. 21 | */ 22 | export function getOwnEnumerableNonNumericKeysES6< 23 | T extends Record 24 | >(obj: T): StringKeyOf[] { 25 | return Object.getOwnPropertyNames(obj).filter((key) => { 26 | return obj.propertyIsEnumerable(key) && isNonNumericKey(key); 27 | }) as StringKeyOf[]; 28 | } 29 | 30 | /** 31 | * Get all own enumerable string (non-numeric) keys of an object. 32 | * Implemented in terms of methods available in ES5. 33 | * The order of the result is *most likely* to be in the same order in which the 34 | * properties were added to the object, due to de-facto standards in most JS 35 | * runtime environments' implementations of Object.keys 36 | * @param obj - An object. 37 | * @return A list of all the object's own enumerable string (non-numeric) keys. 38 | */ 39 | export function getOwnEnumerableNonNumericKeysES5< 40 | T extends Record 41 | >(obj: T): StringKeyOf[] { 42 | return Object.keys(obj).filter(isNonNumericKey) as StringKeyOf[]; 43 | } 44 | 45 | /** 46 | * Get all own enumerable string (non-numeric) keys of an object. 47 | * Implemented in terms of methods available in ES3. 48 | * The order of the result is *most likely* to be in the same order in which the 49 | * properties were added to the object, due to de-facto standards in most JS 50 | * runtime environments' implementations of for/in object key iteration. 51 | * @param obj - An object. 52 | * @return A list of all the object's own enumerable string (non-numeric) keys. 53 | */ 54 | export function getOwnEnumerableNonNumericKeysES3< 55 | T extends Record 56 | >(obj: T): StringKeyOf[] { 57 | const result: StringKeyOf[] = []; 58 | 59 | for (const key in obj) { 60 | if ( 61 | obj.hasOwnProperty(key) && 62 | obj.propertyIsEnumerable(key) && 63 | isNonNumericKey(key) 64 | ) { 65 | result.push(key); 66 | } 67 | } 68 | 69 | return result; 70 | } 71 | 72 | /** 73 | * Get all own enumerable string (non-numeric) keys of an object, using 74 | * the best implementation available. 75 | * The order of the result is either *guaranteed* or *most likely* to be in the 76 | * same order in which the properties were added to the object, depending on 77 | * the best available implementation. 78 | * @see getEnumerableStringKeysES6 79 | * @see getEnumerableStringKeysES5 80 | * @see getEnumerableStringKeysES3 81 | * @param obj - An object. 82 | * @return A list of all the object's own enumerable string (non-numeric) keys. 83 | */ 84 | export const getOwnEnumerableNonNumericKeys = !!Object.getOwnPropertyNames 85 | ? getOwnEnumerableNonNumericKeysES6 86 | : !!Object.keys 87 | ? getOwnEnumerableNonNumericKeysES5 88 | : getOwnEnumerableNonNumericKeysES3; 89 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/visitValue/number-enum-both.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | enum RGB { 4 | R, 5 | G, 6 | B 7 | } 8 | 9 | declare const rgb: RGB | null | undefined; 10 | 11 | // Test param types 12 | $enum.visitValue(rgb).with({ 13 | [RGB.R]: (value) => { 14 | // $ExpectType RGB.R 15 | value; 16 | }, 17 | [RGB.G]: (value) => { 18 | // $ExpectType RGB.G 19 | value; 20 | }, 21 | [RGB.B]: (value) => { 22 | // $ExpectType RGB.B 23 | value; 24 | }, 25 | [$enum.handleNull]: (value) => { 26 | // $ExpectType null 27 | value; 28 | }, 29 | [$enum.handleUndefined]: (value) => { 30 | // $ExpectType undefined 31 | value; 32 | }, 33 | [$enum.handleUnexpected]: (value) => { 34 | // $ExpectType number 35 | value; 36 | } 37 | }); 38 | 39 | // handleUnexpected is optional 40 | $enum.visitValue(rgb).with({ 41 | [RGB.R]: (value) => {}, 42 | [RGB.G]: (value) => {}, 43 | [RGB.B]: (value) => {}, 44 | [$enum.handleNull]: (value) => {}, 45 | [$enum.handleUndefined]: (value) => {} 46 | }); 47 | 48 | // Return type is inferred 49 | // $ExpectType number 50 | $enum.visitValue(rgb).with({ 51 | [RGB.R]: (value) => 10, 52 | [RGB.G]: (value) => 20, 53 | [RGB.B]: (value) => 30, 54 | [$enum.handleNull]: (value) => -1, 55 | [$enum.handleUndefined]: (value) => -1 56 | }); 57 | // $ExpectType string 58 | $enum.visitValue(rgb).with({ 59 | [RGB.R]: (value) => "10", 60 | [RGB.G]: (value) => "20", 61 | [RGB.B]: (value) => "30", 62 | [$enum.handleNull]: (value) => "-1", 63 | [$enum.handleUndefined]: (value) => "-1" 64 | }); 65 | 66 | // Return type is inferred when "unhandled" entries exist 67 | // $ExpectType number 68 | $enum.visitValue(rgb).with({ 69 | [RGB.R]: (value) => 10, 70 | [RGB.G]: $enum.unhandledEntry, 71 | [RGB.B]: (value) => 30, 72 | [$enum.handleNull]: (value) => -1, 73 | [$enum.handleUndefined]: (value) => -1 74 | }); 75 | 76 | // special handlers can be unhandled 77 | // $ExpectType number 78 | $enum.visitValue(rgb).with({ 79 | [RGB.R]: (value) => 10, 80 | [RGB.G]: (value) => 20, 81 | [RGB.B]: (value) => 30, 82 | [$enum.handleNull]: $enum.unhandledEntry, 83 | [$enum.handleUndefined]: $enum.unhandledEntry, 84 | [$enum.handleUnexpected]: $enum.unhandledEntry 85 | }); 86 | 87 | // Missing value handler causes error 88 | // $ExpectError 89 | $enum.visitValue(rgb).with({ 90 | [RGB.R]: (value) => {}, 91 | [RGB.B]: (value) => {}, 92 | [$enum.handleNull]: (value) => {}, 93 | [$enum.handleUndefined]: (value) => {} 94 | }); 95 | 96 | // Missing null handler causes error 97 | // $ExpectError 98 | $enum.visitValue(rgb).with({ 99 | [RGB.R]: (value) => {}, 100 | [RGB.G]: (value) => {}, 101 | [RGB.B]: (value) => {}, 102 | [$enum.handleUndefined]: (value) => {} 103 | }); 104 | 105 | // Missing undefined handler causes error 106 | // $ExpectError 107 | $enum.visitValue(rgb).with({ 108 | [RGB.R]: (value) => {}, 109 | [RGB.G]: (value) => {}, 110 | [RGB.B]: (value) => {}, 111 | [$enum.handleNull]: (value) => {} 112 | }); 113 | 114 | // Unexpected value handler causes error 115 | $enum.visitValue(rgb).with({ 116 | [RGB.R]: (value) => {}, 117 | // $ExpectError 118 | oops: (value) => {}, 119 | [RGB.G]: (value) => {}, 120 | [RGB.B]: (value) => {}, 121 | [$enum.handleNull]: (value) => {}, 122 | [$enum.handleUndefined]: (value) => {} 123 | }); 124 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/visitValue/mixed-enum-both.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | enum RGB { 4 | R = 1, 5 | G = "g", 6 | B = 3 7 | } 8 | 9 | declare const rgb: RGB | null | undefined; 10 | 11 | // Test param types 12 | $enum.visitValue(rgb).with({ 13 | [RGB.R]: (value) => { 14 | // $ExpectType RGB.R 15 | value; 16 | }, 17 | [RGB.G]: (value) => { 18 | // $ExpectType RGB.G 19 | value; 20 | }, 21 | [RGB.B]: (value) => { 22 | // $ExpectType RGB.B 23 | value; 24 | }, 25 | [$enum.handleNull]: (value) => { 26 | // $ExpectType null 27 | value; 28 | }, 29 | [$enum.handleUndefined]: (value) => { 30 | // $ExpectType undefined 31 | value; 32 | }, 33 | [$enum.handleUnexpected]: (value) => { 34 | // $ExpectType string | number 35 | value; 36 | } 37 | }); 38 | 39 | // handleUnexpected is optional 40 | $enum.visitValue(rgb).with({ 41 | [RGB.R]: (value) => {}, 42 | [RGB.G]: (value) => {}, 43 | [RGB.B]: (value) => {}, 44 | [$enum.handleNull]: (value) => {}, 45 | [$enum.handleUndefined]: (value) => {} 46 | }); 47 | 48 | // Return type is inferred 49 | // $ExpectType number 50 | $enum.visitValue(rgb).with({ 51 | [RGB.R]: (value) => 10, 52 | [RGB.G]: (value) => 20, 53 | [RGB.B]: (value) => 30, 54 | [$enum.handleNull]: (value) => -1, 55 | [$enum.handleUndefined]: (value) => -1 56 | }); 57 | // $ExpectType string 58 | $enum.visitValue(rgb).with({ 59 | [RGB.R]: (value) => "10", 60 | [RGB.G]: (value) => "20", 61 | [RGB.B]: (value) => "30", 62 | [$enum.handleNull]: (value) => "-1", 63 | [$enum.handleUndefined]: (value) => "-1" 64 | }); 65 | 66 | // Return type is inferred when "unhandled" entries exist 67 | // $ExpectType number 68 | $enum.visitValue(rgb).with({ 69 | [RGB.R]: (value) => 10, 70 | [RGB.G]: $enum.unhandledEntry, 71 | [RGB.B]: (value) => 30, 72 | [$enum.handleNull]: (value) => -1, 73 | [$enum.handleUndefined]: (value) => -1 74 | }); 75 | 76 | // special handlers can be unhandled 77 | // $ExpectType number 78 | $enum.visitValue(rgb).with({ 79 | [RGB.R]: (value) => 10, 80 | [RGB.G]: (value) => 20, 81 | [RGB.B]: (value) => 30, 82 | [$enum.handleNull]: $enum.unhandledEntry, 83 | [$enum.handleUndefined]: $enum.unhandledEntry, 84 | [$enum.handleUnexpected]: $enum.unhandledEntry 85 | }); 86 | 87 | // Missing value handler causes error 88 | // $ExpectError 89 | $enum.visitValue(rgb).with({ 90 | [RGB.R]: (value) => {}, 91 | [RGB.B]: (value) => {}, 92 | [$enum.handleNull]: (value) => {}, 93 | [$enum.handleUndefined]: (value) => {} 94 | }); 95 | 96 | // Missing null handler causes error 97 | // $ExpectError 98 | $enum.visitValue(rgb).with({ 99 | [RGB.R]: (value) => {}, 100 | [RGB.G]: (value) => {}, 101 | [RGB.B]: (value) => {}, 102 | [$enum.handleUndefined]: (value) => {} 103 | }); 104 | 105 | // Missing undefined handler causes error 106 | // $ExpectError 107 | $enum.visitValue(rgb).with({ 108 | [RGB.R]: (value) => {}, 109 | [RGB.G]: (value) => {}, 110 | [RGB.B]: (value) => {}, 111 | [$enum.handleNull]: (value) => {} 112 | }); 113 | 114 | // Unexpected value handler causes error 115 | $enum.visitValue(rgb).with({ 116 | [RGB.R]: (value) => {}, 117 | // $ExpectError 118 | oops: (value) => {}, 119 | [RGB.G]: (value) => {}, 120 | [RGB.B]: (value) => {}, 121 | [$enum.handleNull]: (value) => {}, 122 | [$enum.handleUndefined]: (value) => {} 123 | }); 124 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/visitValue/string-enum-both.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | enum RGB { 4 | R = "r", 5 | G = "g", 6 | B = "b" 7 | } 8 | 9 | declare const rgb: RGB | null | undefined; 10 | 11 | // Test param types 12 | $enum.visitValue(rgb).with({ 13 | [RGB.R]: (value) => { 14 | // $ExpectType RGB.R 15 | value; 16 | }, 17 | [RGB.G]: (value) => { 18 | // $ExpectType RGB.G 19 | value; 20 | }, 21 | [RGB.B]: (value) => { 22 | // $ExpectType RGB.B 23 | value; 24 | }, 25 | [$enum.handleNull]: (value) => { 26 | // $ExpectType null 27 | value; 28 | }, 29 | [$enum.handleUndefined]: (value) => { 30 | // $ExpectType undefined 31 | value; 32 | }, 33 | [$enum.handleUnexpected]: (value) => { 34 | // $ExpectType string 35 | value; 36 | } 37 | }); 38 | 39 | // handleUnexpected is optional 40 | $enum.visitValue(rgb).with({ 41 | [RGB.R]: (value) => {}, 42 | [RGB.G]: (value) => {}, 43 | [RGB.B]: (value) => {}, 44 | [$enum.handleNull]: (value) => {}, 45 | [$enum.handleUndefined]: (value) => {} 46 | }); 47 | 48 | // Return type is inferred 49 | // $ExpectType number 50 | $enum.visitValue(rgb).with({ 51 | [RGB.R]: (value) => 10, 52 | [RGB.G]: (value) => 20, 53 | [RGB.B]: (value) => 30, 54 | [$enum.handleNull]: (value) => -1, 55 | [$enum.handleUndefined]: (value) => -1 56 | }); 57 | // $ExpectType string 58 | $enum.visitValue(rgb).with({ 59 | [RGB.R]: (value) => "10", 60 | [RGB.G]: (value) => "20", 61 | [RGB.B]: (value) => "30", 62 | [$enum.handleNull]: (value) => "-1", 63 | [$enum.handleUndefined]: (value) => "-1" 64 | }); 65 | 66 | // Return type is inferred when "unhandled" entries exist 67 | // $ExpectType number 68 | $enum.visitValue(rgb).with({ 69 | [RGB.R]: (value) => 10, 70 | [RGB.G]: $enum.unhandledEntry, 71 | [RGB.B]: (value) => 30, 72 | [$enum.handleNull]: (value) => -1, 73 | [$enum.handleUndefined]: (value) => -1 74 | }); 75 | 76 | // special handlers can be unhandled 77 | // $ExpectType number 78 | $enum.visitValue(rgb).with({ 79 | [RGB.R]: (value) => 10, 80 | [RGB.G]: (value) => 20, 81 | [RGB.B]: (value) => 30, 82 | [$enum.handleNull]: $enum.unhandledEntry, 83 | [$enum.handleUndefined]: $enum.unhandledEntry, 84 | [$enum.handleUnexpected]: $enum.unhandledEntry 85 | }); 86 | 87 | // Missing value handler causes error 88 | // $ExpectError 89 | $enum.visitValue(rgb).with({ 90 | [RGB.R]: (value) => {}, 91 | [RGB.B]: (value) => {}, 92 | [$enum.handleNull]: (value) => {}, 93 | [$enum.handleUndefined]: (value) => {} 94 | }); 95 | 96 | // Missing null handler causes error 97 | // $ExpectError 98 | $enum.visitValue(rgb).with({ 99 | [RGB.R]: (value) => {}, 100 | [RGB.G]: (value) => {}, 101 | [RGB.B]: (value) => {}, 102 | [$enum.handleUndefined]: (value) => {} 103 | }); 104 | 105 | // Missing undefined handler causes error 106 | // $ExpectError 107 | $enum.visitValue(rgb).with({ 108 | [RGB.R]: (value) => {}, 109 | [RGB.G]: (value) => {}, 110 | [RGB.B]: (value) => {}, 111 | [$enum.handleNull]: (value) => {} 112 | }); 113 | 114 | // Unexpected value handler causes error 115 | $enum.visitValue(rgb).with({ 116 | [RGB.R]: (value) => {}, 117 | // $ExpectError 118 | oops: (value) => {}, 119 | [RGB.G]: (value) => {}, 120 | [RGB.B]: (value) => {}, 121 | [$enum.handleNull]: (value) => {}, 122 | [$enum.handleUndefined]: (value) => {} 123 | }); 124 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ts-enum-util", 3 | "version": "4.1.0", 4 | "description": "TypeScript Enum Utilities", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/UselessPickles/ts-enum-util.git" 8 | }, 9 | "homepage": "https://github.com/UselessPickles/ts-enum-util", 10 | "bugs": { 11 | "url": "https://github.com/UselessPickles/ts-enum-util/issues" 12 | }, 13 | "files": [ 14 | "dist", 15 | "src" 16 | ], 17 | "main": "dist/commonjs/index.js", 18 | "jsnext:main": "dist/es/index.js", 19 | "module": "dist/es/index.js", 20 | "types": "dist/types/index.d.ts", 21 | "scripts": { 22 | "clean:dist": "rimraf dist", 23 | "clean:coverage": "rimraf coverage", 24 | "clean:pack": "rimraf ts-enum-util-*.tgz", 25 | "compile": "tsc --project tsconfig.json --noEmit --pretty --noErrorTruncation", 26 | "lint": "tslint --config tslint.json --project tsconfig.json", 27 | "lint:fix": "npm run lint -- --fix", 28 | "prettier:test": "prettier --list-different \"{src,tests,type_tests}/**/*.ts\"", 29 | "prettier:fix": "prettier --write \"{src,tests,type_tests}/**/*.ts\"", 30 | "build:types": "tsc --project src/tsconfig.json --pretty --noErrorTruncation --emitDeclarationOnly true --declarationMap true --outDir dist/types", 31 | "build:commonjs": "tsc --project src/tsconfig.json --pretty --removeComments --noErrorTruncation --declaration false --outDir dist/commonjs", 32 | "build:es": "tsc --project src/tsconfig.json --pretty --removeComments --noErrorTruncation --declaration false -m es6 --outDir dist/es", 33 | "build": "npm run clean:dist && run-p build:types build:es build:commonjs", 34 | "pack": "run-p clean:pack build && npm pack", 35 | "jest": "jest", 36 | "jest:coverage": "npm run clean:coverage && jest --coverage", 37 | "dtslint:v2_9_plus": "dtslint --expectOnly --localTs node_modules/typescript/lib type_tests/v2_9_plus", 38 | "dtslint": "run-s clean:dist build:types dtslint:v2_9_plus", 39 | "test": "run-s compile prettier:test lint dtslint jest", 40 | "test:coverage": "run-s compile prettier:test lint dtslint jest:coverage", 41 | "build:travis": "run-p test:coverage build && cat ./coverage/lcov.info | ./node_modules/coveralls/bin/coveralls.js" 42 | }, 43 | "author": { 44 | "name": "Jeff Lau", 45 | "email": "jlau@uselesspickles.com", 46 | "url": "http://www.uselesspickles.com" 47 | }, 48 | "license": "MIT", 49 | "devDependencies": { 50 | "@types/jest": "26.0.18", 51 | "coveralls": "3.1.0", 52 | "dtslint": "4.0.7", 53 | "jest": "30.2.0", 54 | "npm-run-all": "4.1.5", 55 | "prettier": "1.19.1", 56 | "rimraf": "3.0.2", 57 | "ts-jest": "26.4.4", 58 | "tslint": "5.20.1", 59 | "tslint-config-prettier": "1.18.0", 60 | "typescript": "4.1.2" 61 | }, 62 | "keywords": [ 63 | "typescript", 64 | "string", 65 | "number", 66 | "literal", 67 | "union", 68 | "enum", 69 | "util" 70 | ], 71 | "jest": { 72 | "cacheDirectory": "./jest_cache", 73 | "transform": { 74 | "^.+\\.ts$": "ts-jest" 75 | }, 76 | "collectCoverageFrom": [ 77 | "src/**/*.{js,ts}", 78 | "!**/node_modules/**" 79 | ], 80 | "coverageReporters": [ 81 | "text", 82 | "lcov" 83 | ], 84 | "testRegex": "((\\.|/)(test|spec))\\.(ts|js)$", 85 | "moduleFileExtensions": [ 86 | "ts", 87 | "js" 88 | ] 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/EnumValueMapper.ts: -------------------------------------------------------------------------------- 1 | import { 2 | handleUnexpected, 3 | handleNull, 4 | handleUndefined, 5 | unhandledEntry 6 | } from "./symbols"; 7 | 8 | /** 9 | * Core definition of all enum value mapper interfaces. 10 | * Defines properties for each possible value of type `E`. 11 | * 12 | * @template E - An enum type or string/number literal union type. 13 | * @template T - The type of the value that the enum value is mapped to. 14 | */ 15 | export type EnumValueMapperCore = { 16 | [P in E]: T | typeof unhandledEntry; 17 | }; 18 | 19 | /** 20 | * Interface for an object that optionally maps an unexpected value to a value 21 | * of type T. 22 | * This is never used by itself, but combined with {@link EnumValueMapperCore}. 23 | * 24 | * @template T - The type of the value that the enum value is mapped to. 25 | */ 26 | export interface UnexpectedEnumValueMapper { 27 | [handleUnexpected]?: T | typeof unhandledEntry; 28 | } 29 | 30 | /** 31 | * Interface for an object that maps a null value to a value of type T. 32 | * This is never used by itself, but combined with {@link EnumValueMapper} as 33 | * needed. 34 | * 35 | * @template T - The type of the value that the enum value is mapped to. 36 | */ 37 | export interface NullEnumValueMapper { 38 | [handleNull]: T | typeof unhandledEntry; 39 | } 40 | 41 | /** 42 | * Interface for an object that maps an undefined value to a value of type T. 43 | * This is never used by itself, but combined with {@link EnumValueMapper} as 44 | * needed. 45 | * 46 | * @template T - The type of the value that the enum value is mapped to. 47 | */ 48 | export interface UndefinedEnumValueMapper { 49 | [handleUndefined]: T | typeof unhandledEntry; 50 | } 51 | 52 | /** 53 | * Interface for an object that maps an enum or string/number literal value to a 54 | * value of type T. 55 | * 56 | * @template E - An enum type or string/number literal union type. 57 | * @template T - The type of the value that the enum value is mapped to. 58 | */ 59 | export type EnumValueMapper = EnumValueMapperCore< 60 | E, 61 | T 62 | > & 63 | UnexpectedEnumValueMapper; 64 | 65 | /** 66 | * Combines {@link EnumValueMapper} with {@link NullEnumValueMapper} for mapping an enum 67 | * or string/number literal value that may be null. 68 | * 69 | * @template E - An enum type or string/number literal union type. 70 | * @template T - The type of the value that the enum value is mapped to. 71 | */ 72 | export type EnumValueMapperWithNull< 73 | E extends string | number, 74 | T 75 | > = EnumValueMapper & 76 | NullEnumValueMapper & 77 | UnexpectedEnumValueMapper; 78 | 79 | /** 80 | * Combines {@link EnumValueMapper} with {@link UndefinedEnumValueMapper} for mapping an 81 | * enum or string/number literal value that may be undefined. 82 | * 83 | * @template E - An enum type or string/number literal union type. 84 | * @template T - The type of the value that the enum value is mapped to. 85 | */ 86 | export type EnumValueMapperWithUndefined< 87 | E extends string | number, 88 | T 89 | > = EnumValueMapper & 90 | UndefinedEnumValueMapper & 91 | UnexpectedEnumValueMapper; 92 | 93 | /** 94 | * Combines {@link EnumValueMapper} with {@link NullEnumValueMapper} and 95 | * {@link UndefinedEnumValueMapper} 96 | * for mapping an enum or string/number literal value that may be null or 97 | * undefined. 98 | * 99 | * @template E - An enum type or string/number literal union type. 100 | * @template T - The type of the value that the enum value is mapped to. 101 | */ 102 | export type EnumValueMapperWithNullAndUndefined< 103 | E extends string | number, 104 | T 105 | > = EnumValueMapper & 106 | NullEnumValueMapper & 107 | UndefinedEnumValueMapper & 108 | UnexpectedEnumValueMapper; 109 | -------------------------------------------------------------------------------- /docs/migration_from_ts-string-visitor.md: -------------------------------------------------------------------------------- 1 | # Migration Guide: from `ts-string-visitor` 2 | 3 | The functionality of `ts-string-visitor` 4 | ([npm](https://www.npmjs.com/package/ts-string-visitor), 5 | [github](https://github.com/UselessPickles/ts-string-visitor)) was merged into 6 | `ts-enum-util` v4. This guide will help you convert existing `ts-string-visitor`-based 7 | code to use equivalent functionality in `ts-enum-util`. 8 | 9 | # Contents 10 | 11 | 12 | 13 | - [By Example](#by-example) 14 | - [String Visitor](#string-visitor) 15 | - [String Mapper](#string-mapper) 16 | - [Notes About Differences](#notes-about-differences) 17 | - [Numeric Values Now Supported!](#numeric-values-now-supported) 18 | - [Simplified imports](#simplified-imports) 19 | - [Special keys for handling `null`/`undefined`/unexpected values](#special-keys-for-handling-nullundefinedunexpected-values) 20 | 21 | 22 | 23 | ## By Example 24 | 25 | Here's a couple simple code examples of `ts-string-visitor` code and equivalent 26 | code using `ts-enum-util` to demonstrate the differences. 27 | 28 | All examples assume a variable named `value` of type `RGB | null |undefined` 29 | exists, where `RGB` is defined as: 30 | 31 | ```ts 32 | enum RGB { 33 | R = "r", 34 | G = "g", 35 | B = "b" 36 | } 37 | ``` 38 | 39 | ### String Visitor 40 | 41 | Using `ts-string-visitor`: 42 | 43 | ```ts 44 | import { visitString } from "ts-string-visitor"; 45 | 46 | visitString(value).with({ 47 | [RGB.R]: () => {}, 48 | // explicitly unhandled entry 49 | [RGB.G]: visitString.unhandled, 50 | [RGB.B]: () => {}, 51 | handleNull: () => {}, 52 | handleUndefined: () => {}, 53 | handleUnexpected: () => {} 54 | }); 55 | ``` 56 | 57 | Using `ts-enum-util`: 58 | 59 | ```ts 60 | import { $enum } from "ts-enum-util"; 61 | 62 | $enum.visitValue(value).with({ 63 | [RGB.R]: () => {}, 64 | // explicitly unhandled entry 65 | [RGB.G]: $enum.unhandledEntry, 66 | [RGB.B]: () => {}, 67 | [$enum.handleNull]: () => {}, 68 | [$enum.handleUndefined]: () => {}, 69 | [$enum.handleUnexpected]: () => {} 70 | }); 71 | ``` 72 | 73 | ### String Mapper 74 | 75 | Using `ts-string-visitor`: 76 | 77 | ```ts 78 | import { mapString } from "ts-string-visitor"; 79 | 80 | const result = mapString(value).with({ 81 | [RGB.R]: 1, 82 | // explicitly unhandled entry 83 | [RGB.G]: mapString.unhandled, 84 | [RGB.B]: 2, 85 | handleNull: 3, 86 | handleUndefined: 4, 87 | handleUnexpected: 5 88 | }); 89 | ``` 90 | 91 | Using `ts-enum-util`: 92 | 93 | ```ts 94 | import { $enum } from "ts-enum-util"; 95 | 96 | const result = $enum.mapValue(value).with({ 97 | [RGB.R]: 1, 98 | // explicitly unhandled entry 99 | [RGB.G]: $enum.unhandledEntry, 100 | [RGB.B]: 2, 101 | [$enum.handleNull]: 3, 102 | [$enum.handleUndefined]: 4, 103 | [$enum.handleUnexpected]: 5 104 | }); 105 | ``` 106 | 107 | ## Notes About Differences 108 | 109 | ### Numeric Values Now Supported! 110 | 111 | `ts-string-visitor` only supported visiting/mapping string literal/enum types. Thanks to advancements in key types in TypeScript 2.9, `ts-enum-util`'s Value Visitor/Mapper functionality now supports numeric literal/enum types too! 112 | 113 | ### Simplified imports 114 | 115 | Everything now is now conveniently accessible as a property of `$enum`, so there's 116 | less for you to remember. Just start typing `$enum.` and your IDE should be able 117 | to help you with adding the `import` statement and suggesting available 118 | methods/properties of `$enum`. 119 | 120 | ### Special keys for handling `null`/`undefined`/unexpected values 121 | 122 | The special keys for handling `null`, `undefined`, and unexpected values 123 | used to be simple string keys. Now they are `unique symbol` keys, which guarantee 124 | zero chance of collision between the special handler keys and legitimate string 125 | literal or string enum values to be visited/mapped, in case you really are processing a value that could literally be `"handleNull"`, etc. 126 | -------------------------------------------------------------------------------- /src/mapEnumValue.ts: -------------------------------------------------------------------------------- 1 | import { 2 | EnumValueMappee, 3 | EnumValueMappeeWithNull, 4 | EnumValueMappeeWithUndefined, 5 | EnumValueMappeeWithNullAndUndefined 6 | } from "./EnumValueMappee"; 7 | 8 | /** 9 | * Union of all "EnumValueMappee" types. 10 | */ 11 | type AnyEnumValueMappee = 12 | | EnumValueMappee 13 | | EnumValueMappeeWithNull 14 | | EnumValueMappeeWithUndefined 15 | | EnumValueMappeeWithNullAndUndefined; 16 | 17 | /** 18 | * The first step to mapping the value of an enum or string/number literal type. 19 | * This method creates a "mappee" wrapper object, whose "with()" method must be 20 | * called with a mapper implementation. 21 | * 22 | * Example: mapEnumValue(aStringEnumValue).with({ ... }). 23 | * 24 | * See also, {@link EnumValueMappee#with} and {@link ValueMapper}. 25 | * 26 | * @template E - An enum or string/number literal type. 27 | * 28 | * @param value - The value to visit. Must be an enum or string/number literal. 29 | * @return A "mappee" wrapper around the provided value, whose "with()" method 30 | * must be called with a mapper implementation. 31 | */ 32 | export function mapEnumValue( 33 | value: E 34 | ): EnumValueMappee; 35 | /** 36 | * The first step to mapping the value of an enum or string/number literal type. 37 | * This method creates a "mappee" wrapper object, whose "with()" method must be 38 | * called with a mapper implementation. 39 | * 40 | * Example: mapEnumValue(aStringEnumValue).with({ ... }). 41 | * 42 | * See also, {@link EnumValueMappeeWithNull#with} and {@link ValueMapperWithNull}. 43 | * 44 | * @template E - An enum or string/number literal type. 45 | * 46 | * @param value - The value to visit. Must be an enum or string/number literal. 47 | * @return A "mappee" wrapper around the provided value, whose "with()" method 48 | * must be called with a mapper implementation. 49 | */ 50 | export function mapEnumValue( 51 | value: E | null 52 | ): EnumValueMappeeWithNull; 53 | /** 54 | * The first step to mapping the value of an enum or string/number literal type. 55 | * This method creates a "mappee" wrapper object, whose "with()" method must be 56 | * called with a mapper implementation. 57 | * 58 | * Example: mapEnumValue(aStringEnumValue).with({ ... }). 59 | * 60 | * See also, {@link EnumValueMappeeWithUndefined#with} and 61 | * {@link ValueMapperWithUndefined}. 62 | * 63 | * @template E - An enum or string/number literal type. 64 | * 65 | * @param value - The value to visit. Must be an enum or string/number literal. 66 | * @return A "mappee" wrapper around the provided value, whose "with()" method 67 | * must be called with a mapper implementation. 68 | */ 69 | export function mapEnumValue( 70 | value: E | undefined 71 | ): EnumValueMappeeWithUndefined; 72 | /** 73 | * The first step to mapping the value of an enum or string/number literal type. 74 | * This method creates a "mappee" wrapper object, whose "with()" method must be 75 | * called with a mapper implementation. 76 | * 77 | * Example: mapEnumValue(aStringEnumValue).with({ ... }). 78 | * See also, {@link EnumValueMappeeWithNullAndUndefined#with} and 79 | * {@link ValueMapperWithNullAndUndefined}. 80 | * 81 | * @template E - An enum or string/number literal type. 82 | * 83 | * @param value - The value to visit. Must be an enum or string/number literal. 84 | * @return A "mappee" wrapper around the provided value, whose "with()" method 85 | * must be called with a mapper implementation. 86 | */ 87 | export function mapEnumValue( 88 | value: E | null | undefined 89 | ): EnumValueMappeeWithNullAndUndefined; 90 | 91 | export function mapEnumValue( 92 | value: E | null | undefined 93 | ): AnyEnumValueMappee { 94 | // NOTE: The run time type of EnumValueMappee created does not necessarily match 95 | // the compile-time type. This results in unusual EnumValueMappee.with() 96 | // implementations. 97 | if (value === null) { 98 | return new EnumValueMappeeWithNull(); 99 | } else if (value === undefined) { 100 | return new EnumValueMappeeWithUndefined(); 101 | } else { 102 | return new EnumValueMappee(value); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/visitEnumValue.ts: -------------------------------------------------------------------------------- 1 | import { 2 | EnumValueVisitee, 3 | EnumValueVisiteeWithNull, 4 | EnumValueVisiteeWithUndefined, 5 | EnumValueVisiteeWithNullAndUndefined 6 | } from "./EnumValueVisitee"; 7 | 8 | /** 9 | * Union of all "EnumValueVisitee" types. 10 | */ 11 | type AnyEnumValueVisitee = 12 | | EnumValueVisitee 13 | | EnumValueVisiteeWithNull 14 | | EnumValueVisiteeWithUndefined 15 | | EnumValueVisiteeWithNullAndUndefined; 16 | 17 | /** 18 | * The first step to mapping the value of an enum or string/number literal type. 19 | * This method creates a "mappee" wrapper object, whose "with()" method must be 20 | * called with a mapper implementation. 21 | * 22 | * Example: visitEnumValue(aStringEnumValue).with({ ... }). 23 | * 24 | * See also, {@link EnumValueVisitee#with} and {@link ValueMapper}. 25 | * 26 | * @template E - An enum or string/number literal type. 27 | * 28 | * @param value - The value to visit. Must be an enum or string/number literal. 29 | * @return A "mappee" wrapper around the provided value, whose "with()" method 30 | * must be called with a mapper implementation. 31 | */ 32 | export function visitEnumValue( 33 | value: E 34 | ): EnumValueVisitee; 35 | /** 36 | * The first step to mapping the value of an enum or string/number literal type. 37 | * This method creates a "mappee" wrapper object, whose "with()" method must be 38 | * called with a mapper implementation. 39 | * 40 | * Example: visitEnumValue(aStringEnumValue).with({ ... }). 41 | * 42 | * See also, {@link EnumValueVisiteeWithNull#with} and {@link ValueMapperWithNull}. 43 | * 44 | * @template E - An enum or string/number literal type. 45 | * 46 | * @param value - The value to visit. Must be an enum or string/number literal. 47 | * @return A "mappee" wrapper around the provided value, whose "with()" method 48 | * must be called with a mapper implementation. 49 | */ 50 | export function visitEnumValue( 51 | value: E | null 52 | ): EnumValueVisiteeWithNull; 53 | /** 54 | * The first step to mapping the value of an enum or string/number literal type. 55 | * This method creates a "mappee" wrapper object, whose "with()" method must be 56 | * called with a mapper implementation. 57 | * 58 | * Example: visitEnumValue(aStringEnumValue).with({ ... }). 59 | * 60 | * See also, {@link EnumValueVisiteeWithUndefined#with} and 61 | * {@link ValueMapperWithUndefined}. 62 | * 63 | * @template E - An enum or string/number literal type. 64 | * 65 | * @param value - The value to visit. Must be an enum or string/number literal. 66 | * @return A "mappee" wrapper around the provided value, whose "with()" method 67 | * must be called with a mapper implementation. 68 | */ 69 | export function visitEnumValue( 70 | value: E | undefined 71 | ): EnumValueVisiteeWithUndefined; 72 | /** 73 | * The first step to mapping the value of an enum or string/number literal type. 74 | * This method creates a "mappee" wrapper object, whose "with()" method must be 75 | * called with a mapper implementation. 76 | * 77 | * Example: visitEnumValue(aStringEnumValue).with({ ... }). 78 | * See also, {@link EnumValueVisiteeWithNullAndUndefined#with} and 79 | * {@link ValueMapperWithNullAndUndefined}. 80 | * 81 | * @template E - An enum or string/number literal type. 82 | * 83 | * @param value - The value to visit. Must be an enum or string/number literal. 84 | * @return A "mappee" wrapper around the provided value, whose "with()" method 85 | * must be called with a mapper implementation. 86 | */ 87 | export function visitEnumValue( 88 | value: E | null | undefined 89 | ): EnumValueVisiteeWithNullAndUndefined; 90 | 91 | export function visitEnumValue( 92 | value: E | null | undefined 93 | ): AnyEnumValueVisitee { 94 | // NOTE: The run time type of EnumValueVisitee created does not necessarily match 95 | // the compile-time type. This results in unusual EnumValueVisitee.with() 96 | // implementations. 97 | if (value === null) { 98 | return new EnumValueVisiteeWithNull(); 99 | } else if (value === undefined) { 100 | return new EnumValueVisiteeWithUndefined(); 101 | } else { 102 | return new EnumValueVisitee(value); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/EnumValueVisitor.ts: -------------------------------------------------------------------------------- 1 | import { 2 | handleUnexpected, 3 | handleNull, 4 | handleUndefined, 5 | unhandledEntry 6 | } from "./symbols"; 7 | 8 | /** 9 | * Helper type to widen a number/string enum/literal type to plain string or number. 10 | */ 11 | export type WidenEnumType = 12 | | (E extends number ? number : never) 13 | | (E extends string ? string : never); 14 | 15 | /** 16 | * Generic method signature for a string visitor handler method. 17 | * @template E - The type of the parameter to the handler. Must be a string literal, null, or undefined. 18 | * @template R - The return type of the handler. Defaults to void. 19 | * @param value - The value being visited by the visitor. 20 | * @returns A result to be returned by the visitor, 21 | */ 22 | export type EnumValueVisitorHandler< 23 | E extends string | number | null | undefined, 24 | R = void 25 | > = (value: E) => R; 26 | 27 | /** 28 | * Core definition of all string visitor interfaces. 29 | * Defines visitor handler properties for each possible value of type `E`. 30 | * 31 | * @template E - A string literal type or string enum type. 32 | * @template R - The return type of the visitor methods. 33 | */ 34 | export type EnumValueVisitorCore = { 35 | [P in E]: EnumValueVisitorHandler | typeof unhandledEntry; 36 | }; 37 | 38 | /** 39 | * A visitor interface for visiting a null value. 40 | * This is never used by itself, but combined with {@link EnumValueVisitor} as needed. 41 | * 42 | * @template R - The return type of the visitor method. 43 | */ 44 | export interface NullEnumValueVisitor { 45 | [handleNull]: EnumValueVisitorHandler | typeof unhandledEntry; 46 | } 47 | 48 | /** 49 | * A visitor interface for visiting an undefined value. 50 | * This is never used by itself, but combined with {@link EnumValueVisitor} as needed. 51 | * 52 | * @template R - The return type of the visitor method. 53 | */ 54 | export interface UndefinedEnumValueVisitor { 55 | [handleUndefined]: 56 | | EnumValueVisitorHandler 57 | | typeof unhandledEntry; 58 | } 59 | 60 | /** 61 | * A visitor interface for visiting the value of a string literal type or a string enum type. 62 | * 63 | * @template E - A string literal type or string enum type. 64 | * @template R - The return type of the visitor methods. 65 | */ 66 | export type EnumValueVisitor< 67 | E extends string | number, 68 | R 69 | > = EnumValueVisitorCore & { 70 | [handleUnexpected]?: 71 | | EnumValueVisitorHandler | null | undefined, R> 72 | | typeof unhandledEntry; 73 | }; 74 | 75 | /** 76 | * Combines {@link EnumValueVisitor} with {@link NullEnumValueVisitor} for visiting a string literal/enum 77 | * that may be null. 78 | * 79 | * @template E - A string literal type or string enum type. 80 | * @template R - The return type of the visitor methods. 81 | */ 82 | export type EnumValueVisitorWithNull< 83 | E extends string | number, 84 | R 85 | > = EnumValueVisitorCore & 86 | NullEnumValueVisitor & { 87 | [handleUnexpected]?: 88 | | EnumValueVisitorHandler | undefined, R> 89 | | typeof unhandledEntry; 90 | }; 91 | 92 | /** 93 | * Combines {@link EnumValueVisitor} with {@link UndefinedEnumValueVisitor} for visiting a string literal/enum 94 | * that may be undefined. 95 | * 96 | * @template E - A string literal type or string enum type. 97 | * @template R - The return type of the visitor methods. 98 | */ 99 | export type EnumValueVisitorWithUndefined< 100 | E extends string | number, 101 | R 102 | > = EnumValueVisitorCore & 103 | UndefinedEnumValueVisitor & { 104 | [handleUnexpected]?: 105 | | EnumValueVisitorHandler | null, R> 106 | | typeof unhandledEntry; 107 | }; 108 | 109 | /** 110 | * Combines {@link EnumValueVisitor} with {@link NullEnumValueVisitor} and {@link UndefinedEnumValueVisitor} 111 | * for visiting a string literal/enum that may be null or undefined. 112 | * 113 | * @template E - A string literal type or string enum type. 114 | * @template R - The return type of the visitor methods. 115 | */ 116 | export type EnumValueVisitorWithNullAndUndefined< 117 | E extends string | number, 118 | R 119 | > = EnumValueVisitorCore & 120 | NullEnumValueVisitor & 121 | UndefinedEnumValueVisitor & { 122 | [handleUnexpected]?: 123 | | EnumValueVisitorHandler, R> 124 | | typeof unhandledEntry; 125 | }; 126 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm version](https://img.shields.io/npm/v/ts-enum-util.svg)](https://www.npmjs.com/package/ts-enum-util) 2 | [![Join the chat at https://gitter.im/ts-enum-util/Lobby](https://badges.gitter.im/ts-enum-util/Lobby.svg)](https://gitter.im/ts-enum-util/Lobby?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 3 | [![Build Status](https://travis-ci.org/UselessPickles/ts-enum-util.svg?branch=master)](https://travis-ci.org/UselessPickles/ts-enum-util) 4 | [![Coverage Status](https://coveralls.io/repos/github/UselessPickles/ts-enum-util/badge.svg?branch=master)](https://coveralls.io/github/UselessPickles/ts-enum-util?branch=master) 5 | 6 | # ts-enum-util 7 | 8 | Strictly typed utilities for working with TypeScript enums (and string/number literal union types). 9 | 10 | NOTE: Be sure to read about supported TypeScript versions in the [Requirements](#requirements) section. 11 | 12 | # Contents 13 | 14 | 15 | 16 | - [What is it?](#what-is-it) 17 | - [Enum Wrapper Utilities](#enum-wrapper-utilities) 18 | - [Enum Value Visitor/Mapper](#enum-value-visitormapper) 19 | - [Installation](#installation) 20 | - [Getting Started](#getting-started) 21 | - [Usage Documentation/Examples](#usage-documentationexamples) 22 | - [Requirements](#requirements) 23 | - [Why is the main export named `$enum`?](#why-is-the-main-export-named-enum) 24 | 25 | 26 | 27 | ## What is it? 28 | 29 | `ts-enum-util` provides type-safe utilities to improve the usefulness of TypeScript enums. 30 | There are two major and distinct aspects to `ts-enum-util`. 31 | 32 | ### Enum Wrapper Utilities 33 | 34 | A wrapper around an enum, or "enum-like object", that provides a variety of type-safe 35 | utilities in terms of the run-time representation fo the enum's keys/values. Some 36 | examples include: 37 | 38 | - Get a list of an enum's keys, values, or key/value pairs. 39 | - Look up values by key with run-time key validation and optional result defaulting. 40 | - Reverse lookup of keys by value (for string enums too!) with run-time value validation and optional result defaulting. 41 | - Run-time validation that a specified value or key is valid for a given enum, with compile-time type guards. 42 | - Treat an enum similar to an Array of key/value tuples. 43 | - Treat an enum similar to a Map of values. 44 | 45 | All of these utilities are very specifically typed for each enum via generics and type inference. 46 | 47 | ### Enum Value Visitor/Mapper 48 | 49 | A visitor pattern for processing a single value whose type is an enum, or union of 50 | string/number literals. It's like a switch statement that forces you to implement 51 | every possible case (including `null` or `undefined`, if relevant), avoiding bugs 52 | because you forgot to handle one of the enum's values, or because the enum 53 | definition was updated with a new value and you forgot to update existing code to 54 | handle the new value. 55 | 56 | The more generalized "visit" functionallity has you associate a different function 57 | with each possible value of an enum or string/number literal union. 58 | The appropriate function is executed (and its return value returned) based on 59 | which value the argument is at run-time. 60 | 61 | A streamlined "map" functionality has you simply associate values (of any type) 62 | with each possible value of an enum or string/number literal union. 63 | The appropriate mapped value is returned based on which value the argument is at run-time. 64 | 65 | ## Installation 66 | 67 | Install via [NPM](https://www.npmjs.com/package/ts-enum-util): 68 | 69 | ``` 70 | npm i -s ts-enum-util 71 | ``` 72 | 73 | ## Getting Started 74 | 75 | Import `$enum`: 76 | 77 | ```ts 78 | import { $enum } from "ts-enum-util"; 79 | ``` 80 | 81 | Define an `enum`: 82 | 83 | ```ts 84 | enum Color { 85 | R, 86 | G, 87 | B 88 | } 89 | ``` 90 | 91 | Use `$enum()` as a function to access [Enum Wrapper Utilities](./docs/EnumWrapper.md) for your `enum`: 92 | 93 | ```ts 94 | // type of "values": Color[] 95 | // value of "values": [0, 1, 2] 96 | const values = $enum(Color).getValues(); 97 | ``` 98 | 99 | Use `$enum.visitValue()` or `$enum.mapValue()` to access [Enum Value Visitor/Mapper](./docs/EnumValueVisitor.md) functionality: 100 | 101 | ```ts 102 | function doColorAction(color: Color): void { 103 | $enum.visitValue(color).with({ 104 | [Color.R]: () => { 105 | window.alert("Red Alert!"); 106 | }, 107 | [Color.G]: () => { 108 | window.location = "http://google.com"; 109 | }, 110 | [Color.B]: () => { 111 | console.log("Blue"); 112 | } 113 | }); 114 | } 115 | 116 | function getColorLabel(color: Color | undefined): string { 117 | return $enum.mapValue(color).with({ 118 | [Color.R]: "Red", 119 | [Color.G]: "Green", 120 | [Color.B]: "Blue", 121 | [$enum.handleUndefined]: "Unspecified" 122 | }); 123 | } 124 | ``` 125 | 126 | ## Usage Documentation/Examples 127 | 128 | To keep the size of the README under control, usage documentation and examples have 129 | been split out to separate files: 130 | 131 | - [Enum Wrapper Utilities](./docs/EnumWrapper.md) 132 | - [Enum Value Visitor/Mapper](./docs/EnumValueVisitor.md) 133 | - [Migration Guide: from `ts-string-visitor`](./docs/migration_from_ts-string-visitor.md) 134 | 135 | ## Requirements 136 | 137 | - _TypeScript 2.9+_: `ts-enum-util` is all about strictly type-safe utilities 138 | around TypeScript enums, so it would be much less useful in a plain JavaScript 139 | project. More specifically, TypeScript 2.9 included advancements in handling 140 | number literals as property names of object types, which is necessary for 141 | implementing some `ts-enum-util` functionality consistently for both string and 142 | number enum types. 143 | - _Stuck with an older version of TypeScript_? 144 | - For Value Visitor/Mapper functionality, check out `ts-string-visitor` 145 | ([npm](https://www.npmjs.com/package/ts-string-visitor), 146 | [github](https://github.com/UselessPickles/ts-string-visitor)). NOTE: 147 | numeric value visiting/mapping not supported! 148 | - For Enum Wrapper 149 | functionality, check out v3 or v2 of `ts-enum-util`. 150 | - _ES6 Features_: The following ES6 features are used by `ts-enum-util`, so they 151 | must exist (either natively or via polyfill) in the run-time environment: 152 | - `Map` 153 | - `WeakMap` 154 | - `Symbol` 155 | - `Symbol.iterator` 156 | - `Symbol.toStringTag` 157 | 158 | ## Why is the main export named `$enum`? 159 | 160 | I wanted something short, simple, and easy to remember that was unlikely to conflict with anything else so that no one would have to alias it when importing it. By exporting a clear, memorable, and uniquely named "thing", this allows you to simply start writing code that uses `$enum` and most IDEs can take care of inserting the import { \$enum } from "ts-enum-util"; for you (either automatically, or with a quick keyboard shortcut). 161 | 162 | I ended up using inspiration from the naming of jquery's `$()` function. Many javascript developers are familiar with jquery, and the fact that `$()` gives you a wrapper around a raw DOM element to expose additional/simplified functionality around the DOM element. 163 | 164 | Similarly, `$enum()` gives you a wrapper around a raw enum to expose additional/simplified functionality around the enum. 165 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/EnumWrapper/dictionary-number.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | // Dictionary object with number values 4 | declare const TestEnum: { [key: string]: number }; 5 | 6 | declare const str: string; 7 | declare const strOrNull: string | null; 8 | declare const strOrUndefined: string | undefined; 9 | 10 | declare const num: number; 11 | declare const numOrNull: number | null; 12 | declare const numOrUndefined: number | undefined; 13 | 14 | const enumWrapper = $enum(TestEnum); 15 | 16 | // $ExpectType EnumWrapper 17 | enumWrapper; 18 | 19 | // $ExpectType number 20 | enumWrapper.length; 21 | // $ExpectError 22 | enumWrapper.length = 0; // immutable 23 | 24 | // $ExpectType number 25 | enumWrapper.size; 26 | // $ExpectError 27 | enumWrapper.size = 0; // immutable 28 | 29 | // NOTE: Must test via assignability rather than ExpectType because of a change 30 | // in how Readonly tuple types work as of TS 3.1. 31 | // Also cannot test for immutability of items within the entry tuple because of 32 | // this change. 33 | // see: https://github.com/Microsoft/TypeScript/issues/26864 34 | const testEntry: Readonly<[string, number]> = enumWrapper[0]; 35 | // $ExpectError 36 | enumWrapper[0] = ["A", TestEnum.A]; // immutable 37 | 38 | // $ExpectType IterableIterator 39 | enumWrapper.keys(); 40 | 41 | // $ExpectType IterableIterator 42 | enumWrapper.values(); 43 | 44 | // NOTE: Must test via assignability rather than ExpectType because of a change 45 | // in how Readonly tuple types work as of TS 3.1. 46 | // Also cannot test for immutability of items within the iterated entry tuples 47 | // because of this change. 48 | // see: https://github.com/Microsoft/TypeScript/issues/26864 49 | const testEntryIterator: IterableIterator> = enumWrapper.entries(); 52 | for (const entry of enumWrapper.entries()) { 53 | const testIteratedEntry: Readonly<[string, number]> = entry; 54 | } 55 | 56 | // $ExpectType void 57 | enumWrapper.forEach((value, key, collection, index) => { 58 | // $ExpectType number 59 | value; 60 | // $ExpectType string 61 | key; 62 | // $ExpectType EnumWrapper 63 | collection; 64 | // $ExpectType number 65 | index; 66 | 67 | return num; 68 | }); 69 | 70 | // $ExpectType number[] 71 | enumWrapper.map((value, key, collection, index) => { 72 | // $ExpectType number 73 | value; 74 | // $ExpectType string 75 | key; 76 | // $ExpectType EnumWrapper 77 | collection; 78 | // $ExpectType number 79 | index; 80 | 81 | return num; 82 | }); 83 | 84 | // $ExpectType string[] 85 | enumWrapper.getKeys(); 86 | 87 | // $ExpectType number[] 88 | enumWrapper.getValues(); 89 | 90 | // NOTE: Must test via assignability rather than ExpectType because of a change 91 | // in how Readonly tuple types work as of TS 3.1. 92 | // Also cannot test for immutability of items within the entry tuple because of 93 | // this change. 94 | // see: https://github.com/Microsoft/TypeScript/issues/26864 95 | const testEntries: Readonly<[string, number]>[] = enumWrapper.getEntries(); 96 | 97 | // $ExpectType boolean 98 | enumWrapper.isKey(str); 99 | // $ExpectType boolean 100 | enumWrapper.isKey(strOrNull); 101 | // $ExpectType boolean 102 | enumWrapper.isKey(strOrUndefined); 103 | 104 | if (enumWrapper.isKey(str)) { 105 | // $ExpectType string 106 | str; 107 | } 108 | 109 | if (enumWrapper.isKey(strOrNull)) { 110 | // $ExpectType string 111 | strOrNull; 112 | } 113 | 114 | if (enumWrapper.isKey(strOrUndefined)) { 115 | // $ExpectType string 116 | strOrUndefined; 117 | } 118 | 119 | // $ExpectType string 120 | enumWrapper.asKeyOrThrow(str); 121 | // $ExpectType string 122 | enumWrapper.asKeyOrThrow(strOrNull); 123 | // $ExpectType string 124 | enumWrapper.asKeyOrThrow(strOrUndefined); 125 | 126 | // $ExpectType string | undefined 127 | enumWrapper.asKeyOrDefault(str); 128 | // $ExpectType string | undefined 129 | enumWrapper.asKeyOrDefault(strOrNull); 130 | // $ExpectType string | undefined 131 | enumWrapper.asKeyOrDefault(strOrUndefined); 132 | // $ExpectType string | undefined 133 | enumWrapper.asKeyOrDefault(str, undefined); 134 | // $ExpectType string 135 | enumWrapper.asKeyOrDefault(str, str); 136 | // $ExpectType string | undefined 137 | enumWrapper.asKeyOrDefault(str, strOrUndefined); 138 | 139 | // $ExpectType boolean 140 | enumWrapper.isValue(num); 141 | // $ExpectType boolean 142 | enumWrapper.isValue(numOrNull); 143 | // $ExpectType boolean 144 | enumWrapper.isValue(numOrUndefined); 145 | // $ExpectError 146 | enumWrapper.isValue(str); 147 | 148 | if (enumWrapper.isValue(num)) { 149 | // $ExpectType number 150 | num; 151 | } 152 | 153 | if (enumWrapper.isValue(numOrNull)) { 154 | // $ExpectType number 155 | numOrNull; 156 | } 157 | 158 | if (enumWrapper.isValue(numOrUndefined)) { 159 | // $ExpectType number 160 | numOrUndefined; 161 | } 162 | 163 | // $ExpectType number 164 | enumWrapper.asValueOrThrow(num); 165 | // $ExpectType number 166 | enumWrapper.asValueOrThrow(numOrNull); 167 | // $ExpectType number 168 | enumWrapper.asValueOrThrow(numOrUndefined); 169 | // $ExpectError 170 | enumWrapper.asValueOrThrow(str); 171 | 172 | // $ExpectType number | undefined 173 | enumWrapper.asValueOrDefault(num); 174 | // $ExpectType number | undefined 175 | enumWrapper.asValueOrDefault(numOrNull); 176 | // $ExpectType number | undefined 177 | enumWrapper.asValueOrDefault(numOrUndefined); 178 | // $ExpectError 179 | enumWrapper.asValueOrDefault(str); 180 | 181 | // $ExpectType number | undefined 182 | enumWrapper.asValueOrDefault(num, undefined); 183 | // $ExpectType number 184 | enumWrapper.asValueOrDefault(num, num); 185 | // $ExpectType number | undefined 186 | enumWrapper.asValueOrDefault(num, numOrUndefined); 187 | // $ExpectError 188 | enumWrapper.asValueOrDefault(num, str); 189 | 190 | // $ExpectType string 191 | enumWrapper.getKeyOrThrow(num); 192 | // $ExpectType string 193 | enumWrapper.getKeyOrThrow(numOrNull); 194 | // $ExpectType string 195 | enumWrapper.getKeyOrThrow(numOrUndefined); 196 | // $ExpectError 197 | enumWrapper.getKeyOrThrow(str); 198 | 199 | // $ExpectType string | undefined 200 | enumWrapper.getKeyOrDefault(num); 201 | // $ExpectType string | undefined 202 | enumWrapper.getKeyOrDefault(numOrNull); 203 | // $ExpectType string | undefined 204 | enumWrapper.getKeyOrDefault(numOrUndefined); 205 | // $ExpectError 206 | enumWrapper.getKeyOrDefault(str); 207 | 208 | // $ExpectType string 209 | enumWrapper.getKeyOrDefault(num, str); 210 | // $ExpectType string | undefined 211 | enumWrapper.getKeyOrDefault(num, strOrUndefined); 212 | 213 | // $ExpectType number 214 | enumWrapper.getValueOrThrow(str); 215 | // $ExpectType number 216 | enumWrapper.getValueOrThrow(strOrNull); 217 | // $ExpectType number 218 | enumWrapper.getValueOrThrow(strOrUndefined); 219 | 220 | // $ExpectType number | undefined 221 | enumWrapper.getValueOrDefault(str); 222 | // $ExpectType number | undefined 223 | enumWrapper.getValueOrDefault(strOrNull); 224 | // $ExpectType number | undefined 225 | enumWrapper.getValueOrDefault(strOrUndefined); 226 | 227 | // $ExpectType number | undefined 228 | enumWrapper.getValueOrDefault(str, undefined); 229 | // $ExpectType number 230 | enumWrapper.getValueOrDefault(str, num); 231 | // $ExpectType number | undefined 232 | enumWrapper.getValueOrDefault(str, numOrUndefined); 233 | // $ExpectError 234 | enumWrapper.getValueOrDefault(str, str); 235 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/EnumWrapper/dictionary-string.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | // Dictionary object with string values 4 | declare const TestEnum: { [key: string]: string }; 5 | 6 | declare const str: string; 7 | declare const strOrNull: string | null; 8 | declare const strOrUndefined: string | undefined; 9 | 10 | declare const num: number; 11 | declare const numOrNull: number | null; 12 | declare const numOrUndefined: number | undefined; 13 | 14 | const enumWrapper = $enum(TestEnum); 15 | 16 | // $ExpectType EnumWrapper 17 | enumWrapper; 18 | 19 | // $ExpectType number 20 | enumWrapper.length; 21 | // $ExpectError 22 | enumWrapper.length = 0; // immutable 23 | 24 | // $ExpectType number 25 | enumWrapper.size; 26 | // $ExpectError 27 | enumWrapper.size = 0; // immutable 28 | 29 | // NOTE: Must test via assignability rather than ExpectType because of a change 30 | // in how Readonly tuple types work as of TS 3.1. 31 | // Also cannot test for immutability of items within the entry tuple because of 32 | // this change. 33 | // see: https://github.com/Microsoft/TypeScript/issues/26864 34 | const testEntry: Readonly<[string, string]> = enumWrapper[0]; 35 | // $ExpectError 36 | enumWrapper[0] = ["A", TestEnum.A]; // immutable 37 | 38 | // $ExpectType IterableIterator 39 | enumWrapper.keys(); 40 | 41 | // $ExpectType IterableIterator 42 | enumWrapper.values(); 43 | 44 | // NOTE: Must test via assignability rather than ExpectType because of a change 45 | // in how Readonly tuple types work as of TS 3.1. 46 | // Also cannot test for immutability of items within the iterated entry tuples 47 | // because of this change. 48 | // see: https://github.com/Microsoft/TypeScript/issues/26864 49 | const testEntryIterator: IterableIterator> = enumWrapper.entries(); 52 | for (const entry of enumWrapper.entries()) { 53 | const testIteratedEntry: Readonly<[string, string]> = entry; 54 | } 55 | 56 | // $ExpectType void 57 | enumWrapper.forEach((value, key, collection, index) => { 58 | // $ExpectType string 59 | value; 60 | // $ExpectType string 61 | key; 62 | // $ExpectType EnumWrapper 63 | collection; 64 | // $ExpectType number 65 | index; 66 | 67 | return num; 68 | }); 69 | 70 | // $ExpectType number[] 71 | enumWrapper.map((value, key, collection, index) => { 72 | // $ExpectType string 73 | value; 74 | // $ExpectType string 75 | key; 76 | // $ExpectType EnumWrapper 77 | collection; 78 | // $ExpectType number 79 | index; 80 | 81 | return num; 82 | }); 83 | 84 | // $ExpectType string[] 85 | enumWrapper.getKeys(); 86 | 87 | // $ExpectType string[] 88 | enumWrapper.getValues(); 89 | 90 | // NOTE: Must test via assignability rather than ExpectType because of a change 91 | // in how Readonly tuple types work as of TS 3.1. 92 | // Also cannot test for immutability of items within the entry tuple because of 93 | // this change. 94 | // see: https://github.com/Microsoft/TypeScript/issues/26864 95 | const testEntries: Readonly<[string, string]>[] = enumWrapper.getEntries(); 96 | 97 | // $ExpectType boolean 98 | enumWrapper.isKey(str); 99 | // $ExpectType boolean 100 | enumWrapper.isKey(strOrNull); 101 | // $ExpectType boolean 102 | enumWrapper.isKey(strOrUndefined); 103 | 104 | if (enumWrapper.isKey(str)) { 105 | // $ExpectType string 106 | str; 107 | } 108 | 109 | if (enumWrapper.isKey(strOrNull)) { 110 | // $ExpectType string 111 | strOrNull; 112 | } 113 | 114 | if (enumWrapper.isKey(strOrUndefined)) { 115 | // $ExpectType string 116 | strOrUndefined; 117 | } 118 | 119 | // $ExpectType string 120 | enumWrapper.asKeyOrThrow(str); 121 | // $ExpectType string 122 | enumWrapper.asKeyOrThrow(strOrNull); 123 | // $ExpectType string 124 | enumWrapper.asKeyOrThrow(strOrUndefined); 125 | 126 | // $ExpectType string | undefined 127 | enumWrapper.asKeyOrDefault(str); 128 | // $ExpectType string | undefined 129 | enumWrapper.asKeyOrDefault(strOrNull); 130 | // $ExpectType string | undefined 131 | enumWrapper.asKeyOrDefault(strOrUndefined); 132 | // $ExpectType string | undefined 133 | enumWrapper.asKeyOrDefault(str, undefined); 134 | // $ExpectType string 135 | enumWrapper.asKeyOrDefault(str, str); 136 | // $ExpectType string | undefined 137 | enumWrapper.asKeyOrDefault(str, strOrUndefined); 138 | 139 | // $ExpectType boolean 140 | enumWrapper.isValue(str); 141 | // $ExpectType boolean 142 | enumWrapper.isValue(strOrNull); 143 | // $ExpectType boolean 144 | enumWrapper.isValue(strOrUndefined); 145 | // $ExpectError 146 | enumWrapper.isValue(num); 147 | 148 | if (enumWrapper.isValue(str)) { 149 | // $ExpectType string 150 | str; 151 | } 152 | 153 | if (enumWrapper.isValue(strOrNull)) { 154 | // $ExpectType string 155 | strOrNull; 156 | } 157 | 158 | if (enumWrapper.isValue(strOrUndefined)) { 159 | // $ExpectType string 160 | strOrUndefined; 161 | } 162 | 163 | // $ExpectType string 164 | enumWrapper.asValueOrThrow(str); 165 | // $ExpectType string 166 | enumWrapper.asValueOrThrow(strOrNull); 167 | // $ExpectType string 168 | enumWrapper.asValueOrThrow(strOrUndefined); 169 | // $ExpectError 170 | enumWrapper.asValueOrThrow(num); 171 | 172 | // $ExpectType string | undefined 173 | enumWrapper.asValueOrDefault(str); 174 | // $ExpectType string | undefined 175 | enumWrapper.asValueOrDefault(strOrNull); 176 | // $ExpectType string | undefined 177 | enumWrapper.asValueOrDefault(strOrUndefined); 178 | // $ExpectError 179 | enumWrapper.asValueOrDefault(num); 180 | 181 | // $ExpectType string | undefined 182 | enumWrapper.asValueOrDefault(str, undefined); 183 | // $ExpectType string 184 | enumWrapper.asValueOrDefault(str, str); 185 | // $ExpectType string | undefined 186 | enumWrapper.asValueOrDefault(str, strOrUndefined); 187 | // $ExpectError 188 | enumWrapper.asValueOrDefault(str, num); 189 | 190 | // $ExpectType string 191 | enumWrapper.getKeyOrThrow(str); 192 | // $ExpectType string 193 | enumWrapper.getKeyOrThrow(strOrNull); 194 | // $ExpectType string 195 | enumWrapper.getKeyOrThrow(strOrUndefined); 196 | // $ExpectError 197 | enumWrapper.getKeyOrThrow(num); 198 | 199 | // $ExpectType string | undefined 200 | enumWrapper.getKeyOrDefault(str); 201 | // $ExpectType string | undefined 202 | enumWrapper.getKeyOrDefault(strOrNull); 203 | // $ExpectType string | undefined 204 | enumWrapper.getKeyOrDefault(strOrUndefined); 205 | // $ExpectError 206 | enumWrapper.getKeyOrDefault(num); 207 | 208 | // $ExpectType string 209 | enumWrapper.getKeyOrDefault(str, str); 210 | // $ExpectType string | undefined 211 | enumWrapper.getKeyOrDefault(str, strOrUndefined); 212 | 213 | // $ExpectType string 214 | enumWrapper.getValueOrThrow(str); 215 | // $ExpectType string 216 | enumWrapper.getValueOrThrow(strOrNull); 217 | // $ExpectType string 218 | enumWrapper.getValueOrThrow(strOrUndefined); 219 | 220 | // $ExpectType string | undefined 221 | enumWrapper.getValueOrDefault(str); 222 | // $ExpectType string | undefined 223 | enumWrapper.getValueOrDefault(strOrNull); 224 | // $ExpectType string | undefined 225 | enumWrapper.getValueOrDefault(strOrUndefined); 226 | 227 | // $ExpectType string | undefined 228 | enumWrapper.getValueOrDefault(str, undefined); 229 | // $ExpectType string 230 | enumWrapper.getValueOrDefault(str, str); 231 | // $ExpectType string | undefined 232 | enumWrapper.getValueOrDefault(str, strOrUndefined); 233 | // $ExpectError 234 | enumWrapper.getValueOrDefault(str, num); 235 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/EnumWrapper/dictionary-mixed.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | // Dictionary object with a mix of number and string values 4 | declare const TestEnum: { [key: string]: string | number }; 5 | 6 | declare const str: string; 7 | declare const strOrNull: string | null; 8 | declare const strOrUndefined: string | undefined; 9 | 10 | declare const num: number; 11 | declare const numOrNull: number | null; 12 | declare const numOrUndefined: number | undefined; 13 | 14 | declare const numstr: number | string; 15 | declare const numstrOrNull: number | string | null; 16 | declare const numstrOrUndefined: number | string | undefined; 17 | 18 | const enumWrapper = $enum(TestEnum); 19 | 20 | // $ExpectType EnumWrapper 21 | enumWrapper; 22 | 23 | // $ExpectType number 24 | enumWrapper.length; 25 | // $ExpectError 26 | enumWrapper.length = 0; // immutable 27 | 28 | // $ExpectType number 29 | enumWrapper.size; 30 | // $ExpectError 31 | enumWrapper.size = 0; // immutable 32 | 33 | // NOTE: Must test via assignability rather than ExpectType because of a change 34 | // in how Readonly tuple types work as of TS 3.1. 35 | // Also cannot test for immutability of items within the entry tuple because of 36 | // this change. 37 | // see: https://github.com/Microsoft/TypeScript/issues/26864 38 | const testEntry: Readonly<[string, string | number]> = enumWrapper[0]; 39 | // $ExpectError 40 | enumWrapper[0] = ["A", TestEnum.A]; // immutable 41 | 42 | // $ExpectType IterableIterator 43 | enumWrapper.keys(); 44 | 45 | // $ExpectType IterableIterator 46 | enumWrapper.values(); 47 | 48 | // NOTE: Must test via assignability rather than ExpectType because of a change 49 | // in how Readonly tuple types work as of TS 3.1. 50 | // Also cannot test for immutability of items within the iterated entry tuples 51 | // because of this change. 52 | // see: https://github.com/Microsoft/TypeScript/issues/26864 53 | const testEntryIterator: IterableIterator> = enumWrapper.entries(); 56 | for (const entry of enumWrapper.entries()) { 57 | const testIteratedEntry: Readonly<[string, string | number]> = entry; 58 | } 59 | 60 | // $ExpectType void 61 | enumWrapper.forEach((value, key, collection, index) => { 62 | // $ExpectType string | number 63 | value; 64 | // $ExpectType string 65 | key; 66 | // $ExpectType EnumWrapper 67 | collection; 68 | // $ExpectType number 69 | index; 70 | 71 | return num; 72 | }); 73 | 74 | // $ExpectType number[] 75 | enumWrapper.map((value, key, collection, index) => { 76 | // $ExpectType string | number 77 | value; 78 | // $ExpectType string 79 | key; 80 | // $ExpectType EnumWrapper 81 | collection; 82 | // $ExpectType number 83 | index; 84 | 85 | return num; 86 | }); 87 | 88 | // $ExpectType string[] 89 | enumWrapper.getKeys(); 90 | 91 | // $ExpectType (string | number)[] 92 | enumWrapper.getValues(); 93 | 94 | // NOTE: Must test via assignability rather than ExpectType because of a change 95 | // in how Readonly tuple types work as of TS 3.1. 96 | // Also cannot test for immutability of items within the entry tuple because of 97 | // this change. 98 | // see: https://github.com/Microsoft/TypeScript/issues/26864 99 | const testEntries: Readonly< 100 | [string, string | number] 101 | >[] = enumWrapper.getEntries(); 102 | 103 | // $ExpectType boolean 104 | enumWrapper.isKey(str); 105 | // $ExpectType boolean 106 | enumWrapper.isKey(strOrNull); 107 | // $ExpectType boolean 108 | enumWrapper.isKey(strOrUndefined); 109 | 110 | if (enumWrapper.isKey(str)) { 111 | // $ExpectType string 112 | str; 113 | } 114 | 115 | if (enumWrapper.isKey(strOrNull)) { 116 | // $ExpectType string 117 | strOrNull; 118 | } 119 | 120 | if (enumWrapper.isKey(strOrUndefined)) { 121 | // $ExpectType string 122 | strOrUndefined; 123 | } 124 | 125 | // $ExpectType string 126 | enumWrapper.asKeyOrThrow(str); 127 | // $ExpectType string 128 | enumWrapper.asKeyOrThrow(strOrNull); 129 | // $ExpectType string 130 | enumWrapper.asKeyOrThrow(strOrUndefined); 131 | 132 | // $ExpectType string | undefined 133 | enumWrapper.asKeyOrDefault(str); 134 | // $ExpectType string | undefined 135 | enumWrapper.asKeyOrDefault(strOrNull); 136 | // $ExpectType string | undefined 137 | enumWrapper.asKeyOrDefault(strOrUndefined); 138 | // $ExpectType string | undefined 139 | enumWrapper.asKeyOrDefault(str, undefined); 140 | // $ExpectType string 141 | enumWrapper.asKeyOrDefault(str, str); 142 | // $ExpectType string | undefined 143 | enumWrapper.asKeyOrDefault(str, strOrUndefined); 144 | 145 | // $ExpectType boolean 146 | enumWrapper.isValue(numstr); 147 | // $ExpectType boolean 148 | enumWrapper.isValue(numstrOrNull); 149 | // $ExpectType boolean 150 | enumWrapper.isValue(numstrOrUndefined); 151 | 152 | if (enumWrapper.isValue(numstr)) { 153 | // $ExpectType string | number 154 | numstr; 155 | } 156 | 157 | if (enumWrapper.isValue(numstrOrNull)) { 158 | // $ExpectType string | number 159 | numstrOrNull; 160 | } 161 | 162 | if (enumWrapper.isValue(numstrOrUndefined)) { 163 | // $ExpectType string | number 164 | numstrOrUndefined; 165 | } 166 | 167 | // $ExpectType string | number 168 | enumWrapper.asValueOrThrow(numstr); 169 | // $ExpectType string | number 170 | enumWrapper.asValueOrThrow(numstrOrNull); 171 | // $ExpectType string | number 172 | enumWrapper.asValueOrThrow(numstrOrUndefined); 173 | 174 | // $ExpectType string | number | undefined 175 | enumWrapper.asValueOrDefault(numstr); 176 | // $ExpectType string | number | undefined 177 | enumWrapper.asValueOrDefault(numstrOrNull); 178 | // $ExpectType string | number | undefined 179 | enumWrapper.asValueOrDefault(numstrOrUndefined); 180 | 181 | // $ExpectType string | number | undefined 182 | enumWrapper.asValueOrDefault(numstr, undefined); 183 | // $ExpectType string | number 184 | enumWrapper.asValueOrDefault(numstr, num); 185 | // $ExpectType string | number 186 | enumWrapper.asValueOrDefault(numstr, str); 187 | // $ExpectType string | number 188 | enumWrapper.asValueOrDefault(numstr, numstr); 189 | // $ExpectType string | number | undefined 190 | enumWrapper.asValueOrDefault(num, numstrOrUndefined); 191 | 192 | // $ExpectType string 193 | enumWrapper.getKeyOrThrow(numstr); 194 | // $ExpectType string 195 | enumWrapper.getKeyOrThrow(numstrOrNull); 196 | // $ExpectType string 197 | enumWrapper.getKeyOrThrow(numstrOrUndefined); 198 | 199 | // $ExpectType string | undefined 200 | enumWrapper.getKeyOrDefault(numstr); 201 | // $ExpectType string | undefined 202 | enumWrapper.getKeyOrDefault(numstrOrNull); 203 | // $ExpectType string | undefined 204 | enumWrapper.getKeyOrDefault(numstrOrUndefined); 205 | 206 | // $ExpectType string 207 | enumWrapper.getKeyOrDefault(numstr, str); 208 | // $ExpectType string | undefined 209 | enumWrapper.getKeyOrDefault(numstr, strOrUndefined); 210 | 211 | // $ExpectType string | number 212 | enumWrapper.getValueOrThrow(str); 213 | // $ExpectType string | number 214 | enumWrapper.getValueOrThrow(strOrNull); 215 | // $ExpectType string | number 216 | enumWrapper.getValueOrThrow(strOrUndefined); 217 | 218 | // $ExpectType string | number | undefined 219 | enumWrapper.getValueOrDefault(str); 220 | // $ExpectType string | number | undefined 221 | enumWrapper.getValueOrDefault(strOrNull); 222 | // $ExpectType string | number | undefined 223 | enumWrapper.getValueOrDefault(strOrUndefined); 224 | 225 | // $ExpectType string | number | undefined 226 | enumWrapper.getValueOrDefault(str, undefined); 227 | // $ExpectType string | number 228 | enumWrapper.getValueOrDefault(str, num); 229 | // $ExpectType string | number 230 | enumWrapper.getValueOrDefault(str, str); 231 | // $ExpectType string | number 232 | enumWrapper.getValueOrDefault(str, numstr); 233 | // $ExpectType string | number | undefined 234 | enumWrapper.getValueOrDefault(str, numstrOrUndefined); 235 | -------------------------------------------------------------------------------- /src/EnumValueVisitee.ts: -------------------------------------------------------------------------------- 1 | import { 2 | handleUnexpected, 3 | handleNull, 4 | handleUndefined, 5 | unhandledEntry 6 | } from "./symbols"; 7 | import { createUnhandledEntryError } from "./createUnhandledEntryError"; 8 | import { 9 | WidenEnumType, 10 | EnumValueVisitorHandler, 11 | EnumValueVisitorCore, 12 | EnumValueVisitor, 13 | EnumValueVisitorWithNull, 14 | EnumValueVisitorWithUndefined, 15 | EnumValueVisitorWithNullAndUndefined 16 | } from "./EnumValueVisitor"; 17 | 18 | /** 19 | * A wrapper around a string literal or string enum value to be visited. 20 | * Do not use this class directly. Use the {@link visitString} function to get an instance of this class. 21 | * 22 | * @template E - A string literal type or string enum type. 23 | */ 24 | export class EnumValueVisitee { 25 | /** 26 | * Do not use this constructor directly. Use the {@link visitString} function to get an instance of this class. 27 | * @param value - The value to be wrapped by this "visitee". 28 | */ 29 | public constructor(private readonly value: E) {} 30 | 31 | /** 32 | * Visits the wrapped value using the supplied visitor. 33 | * Calls the visitor method whose name matches the wrapped value. 34 | * 35 | * @template R - The return type of the visitor methods. 36 | * 37 | * @param visitor - A visitor implementation for type E that returns type R. 38 | * @returns The return value of the visitor method that is called. 39 | */ 40 | public with(visitor: EnumValueVisitor): R { 41 | if (visitor.hasOwnProperty(this.value)) { 42 | const handler = (visitor as EnumValueVisitorCore)[this.value]; 43 | return processEntry(handler, this.value); 44 | } else if (visitor[handleUnexpected]) { 45 | return processEntry( 46 | visitor[handleUnexpected]!, 47 | (this.value as any) as WidenEnumType 48 | ); 49 | } else { 50 | throw new Error(`Unexpected value: ${this.value}`); 51 | } 52 | } 53 | } 54 | 55 | /** 56 | * A wrapper around a string literal or string enum value to be visited. 57 | * For values that may be null. 58 | * Do not use this class directly. Use the {@link visitString} function to get an instance of this class. 59 | * 60 | * NOTE: At run time, this class is used by {@link visitString} ONLY for handling null values. 61 | * {@link EnumValueVisitee} contains the core run time implementation that is applicable to all 62 | * "EnumValueVisitee" classes. 63 | * 64 | * @template E - A string literal type or string enum type. 65 | */ 66 | export class EnumValueVisiteeWithNull { 67 | /** 68 | * Visits the wrapped value using the supplied visitor. 69 | * If the wrapped value is null, calls the visitor's {@link StringNullVisitor#handleNull} method. 70 | * Otherwise, calls the visitor method whose name matches the wrapped value. 71 | * 72 | * @template R - The return type of the visitor methods. 73 | * 74 | * @param visitor - A visitor implementation for type E that returns type R. 75 | * @returns The return value of the visitor method that is called. 76 | */ 77 | public with(visitor: EnumValueVisitorWithNull): R { 78 | // This class is used at run time for visiting null values regardless of the compile time 79 | // type being visited, so we actually have to check if handleNull exists. 80 | if (visitor[handleNull]) { 81 | return processEntry(visitor[handleNull], null); 82 | } else if (visitor[handleUnexpected]) { 83 | return processEntry( 84 | (visitor as EnumValueVisitor)[handleUnexpected]!, 85 | null 86 | ); 87 | } else { 88 | throw new Error(`Unexpected value: null`); 89 | } 90 | } 91 | } 92 | 93 | /** 94 | * A wrapper around a string literal or string enum value to be visited. 95 | * For values that may be undefined. 96 | * Do not use this class directly. Use the {@link visitString} function to get an instance of this class. 97 | * 98 | * NOTE: At run time, this class is used by {@link visitString} ONLY for handling undefined values. 99 | * {@link EnumValueVisitee} contains the core run time implementation that is applicable to all 100 | * "EnumValueVisitee" classes. 101 | * 102 | * @template E - A string literal type or string enum type. 103 | */ 104 | export class EnumValueVisiteeWithUndefined { 105 | /** 106 | * Visits the wrapped value using the supplied visitor. 107 | * If the wrapped value is undefined, calls the visitor's {@link StringNullVisitor#handleUndefined} method. 108 | * Otherwise, calls the visitor method whose name matches the wrapped value. 109 | * 110 | * @template R - The return type of the visitor methods. 111 | * 112 | * @param visitor - A visitor implementation for type E that returns type R. 113 | * @returns The return value of the visitor method that is called. 114 | */ 115 | public with(visitor: EnumValueVisitorWithUndefined): R { 116 | // This class is used at run time for visiting undefined values regardless of the compile time 117 | // type being visited, so we actually have to check if handleUndefined exists. 118 | if (visitor[handleUndefined]) { 119 | return processEntry(visitor[handleUndefined], undefined); 120 | } else if (visitor[handleUnexpected]) { 121 | return processEntry( 122 | (visitor as EnumValueVisitor)[handleUnexpected]!, 123 | undefined 124 | ); 125 | } else { 126 | throw new Error(`Unexpected value: undefined`); 127 | } 128 | } 129 | } 130 | 131 | /** 132 | * A wrapper around a string literal or string enum value to be visited. 133 | * For values that may be null and undefined. 134 | * Do not use this class directly. Use the {@link visitString} function to get an instance of this class. 135 | * 136 | * NOTE: No run time implementation of this class actually exists. This is only used for compile-time 137 | * typing. {@link EnumValueVisitee} contains the core run time implementation that is applicable to all 138 | * "EnumValueVisitee" classes, while {@link EnumValueVisiteeWithNull} and {@link EnumValueVisiteeWithUndefined} 139 | * are used at run time to handle null and undefined values. 140 | * 141 | * @template E - A string literal type or string enum type. 142 | */ 143 | export declare class EnumValueVisiteeWithNullAndUndefined< 144 | E extends string | number 145 | > { 146 | /** 147 | * Visits the wrapped value using the supplied visitor. 148 | * If the wrapped value is null, calls the visitor's {@link StringNullVisitor#handleNull} method. 149 | * If the wrapped value is undefined, calls the visitor's {@link StringNullVisitor#handleUndefined} method. 150 | * Otherwise, calls the visitor method whose name matches the wrapped value. 151 | * 152 | * @template R - The return type of the visitor methods. 153 | * 154 | * @param visitor - A visitor implementation for type E that returns type R. 155 | * @returns The return value of the visitor method that is called. 156 | */ 157 | public with(visitor: EnumValueVisitorWithNullAndUndefined): R; 158 | } 159 | 160 | /** 161 | * Common implementation for processing an entry of an enum value visitor. 162 | * @param entry - Either the visitor handler implementation for an entry, or an UnhandledEntry. 163 | * @param value - The value being mapped. 164 | * @return The result of executing the provided entry, if it is not an UnhandledEntry. 165 | * @throws {Error} If the provided entry is an UnhandledEntry. 166 | */ 167 | function processEntry( 168 | entry: EnumValueVisitorHandler | typeof unhandledEntry, 169 | value: E 170 | ): R { 171 | if (entry === unhandledEntry) { 172 | throw createUnhandledEntryError(value); 173 | } else { 174 | return entry(value); 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /src/EnumValueMappee.ts: -------------------------------------------------------------------------------- 1 | import { createUnhandledEntryError } from "./createUnhandledEntryError"; 2 | import { 3 | EnumValueMapperCore, 4 | EnumValueMapper, 5 | EnumValueMapperWithNull, 6 | EnumValueMapperWithUndefined, 7 | EnumValueMapperWithNullAndUndefined 8 | } from "./EnumValueMapper"; 9 | import { 10 | handleUnexpected, 11 | handleNull, 12 | handleUndefined, 13 | unhandledEntry 14 | } from "./symbols"; 15 | 16 | /** 17 | * A wrapper around an enum or string/number literal value to be mapped. 18 | * Do not use this class directly. Use the {@link $enum.mapValue} function to 19 | * get an instance of this class. 20 | * 21 | * @template E - An enum or string/number literal type. 22 | */ 23 | export class EnumValueMappee { 24 | /** 25 | * Do not use this constructor directly. Use the {@link $enum.mapValue} 26 | * function to get an instance of this class. 27 | * @param value - The value to be wrapped by this "mappee". 28 | */ 29 | public constructor(private readonly value: E) {} 30 | 31 | /** 32 | * Maps the wrapped value using the supplied mapper. 33 | * Returns the value of the mapper's property whose name matches the wrapped 34 | * value. 35 | * 36 | * @template T - The data type that the enum or string/number literal value 37 | * will be mapped to. 38 | * 39 | * @param mapper - A mapper implementation for type E that returns type T. 40 | * @returns The mapped value from the mapper. 41 | */ 42 | public with(mapper: EnumValueMapper): T { 43 | if (mapper.hasOwnProperty(this.value)) { 44 | return processEntry( 45 | (mapper as EnumValueMapperCore)[this.value], 46 | this.value 47 | ); 48 | } else if (mapper.hasOwnProperty(handleUnexpected)) { 49 | return processEntry(mapper[handleUnexpected]!, this.value); 50 | } else { 51 | throw new Error(`Unexpected value: ${this.value}`); 52 | } 53 | } 54 | } 55 | 56 | /** 57 | * A wrapper around an enum or string/number literal value to be mapped. 58 | * For values that may be null. 59 | * Do not use this class directly. Use the {@link $enum.mapValue} function to 60 | * get an instance of this class. 61 | * 62 | * NOTE: At run time, this class is used by {@link $enum.mapValue} ONLY for 63 | * handling null values. 64 | * {@link EnumValueMappee} contains the core run time implementation that is 65 | * applicable to all "EnumValueMappee" classes. 66 | * 67 | * @template E - An enum or string/number literal type. 68 | */ 69 | export class EnumValueMappeeWithNull { 70 | /** 71 | * Maps the wrapped value using the supplied mapper. 72 | * If the wrapped value is null, returns the mapper's 73 | * {@link handleNull} value. 74 | * Otherwise, returns the value of the mapper's property whose name matches 75 | * the wrapped value. 76 | * 77 | * @template T - The data type that the enum or string/number literal value 78 | * will be mapped to. 79 | * 80 | * @param mapper - A mapper implementation for type E that returns type T. 81 | * @returns The mapped value from the mapper. 82 | */ 83 | public with(mapper: EnumValueMapperWithNull): T { 84 | // This class is used at run time for mapping null values regardless of 85 | // the compile time type being visited, so we actually have to check if 86 | // handleNull exists. 87 | if (mapper.hasOwnProperty(handleNull)) { 88 | return processEntry(mapper[handleNull], null); 89 | } else if (mapper.hasOwnProperty(handleUnexpected)) { 90 | return processEntry(mapper[handleUnexpected]!, null); 91 | } else { 92 | throw new Error(`Unexpected value: null`); 93 | } 94 | } 95 | } 96 | 97 | /** 98 | * A wrapper around an enum or string/number literal value to be mapped. 99 | * For values that may be undefined. 100 | * Do not use this class directly. Use the {@link $enum.mapValue} function to 101 | * get an instance of this class. 102 | * 103 | * NOTE: At run time, this class is used by {@link $enum.mapValue} ONLY for 104 | * handling undefined values. 105 | * {@link EnumValueMappee} contains the core run time implementation that is 106 | * applicable to all "EnumValueMappee" classes. 107 | * 108 | * @template E - An enum or string/number literal type. 109 | */ 110 | export class EnumValueMappeeWithUndefined { 111 | /** 112 | * Maps the wrapped value using the supplied mapper. 113 | * If the wrapped value is undefined, returns the mapper's 114 | * {@link handleUndefined} value. 115 | * Otherwise, returns the value of the mapper's property whose name matches 116 | * the wrapped value. 117 | * 118 | * @template T - The data type that the enum or string/number literal value 119 | * will be mapped to. 120 | * 121 | * @param mapper - A mapper implementation for type E that returns type T. 122 | * @returns The mapped value from the mapper. 123 | */ 124 | public with(mapper: EnumValueMapperWithUndefined): T { 125 | // This class is used at run time for mapping undefined values 126 | // regardless of the compile time type being visited, so we actually 127 | // have to check if handleUndefined exists. 128 | if (mapper.hasOwnProperty(handleUndefined)) { 129 | return processEntry(mapper[handleUndefined], undefined); 130 | } else if (mapper.hasOwnProperty(handleUnexpected)) { 131 | return processEntry(mapper[handleUnexpected]!, undefined); 132 | } else { 133 | throw new Error(`Unexpected value: undefined`); 134 | } 135 | } 136 | } 137 | 138 | /** 139 | * A wrapper around an enum or string/number literal value to be mapped. 140 | * For values that may be null and undefined. 141 | * Do not use this class directly. Use the {@link $enum.mapValue} function to 142 | * get an instance of this class. 143 | * 144 | * NOTE: No run time implementation of this class actually exists. This is only 145 | * used for compile-time typing. 146 | * {@link EnumValueMappee} contains the core run time implementation that is 147 | * applicable to all "EnumValueMappee" classes, while 148 | * {@link EnumValueMappeeWithNull} and {@link EnumValueMappeeWithUndefined} 149 | * are used at run time to handle null and undefined values. 150 | * 151 | * @template E - An enum or string/number literal type. 152 | */ 153 | export declare class EnumValueMappeeWithNullAndUndefined< 154 | E extends string | number 155 | > { 156 | /** 157 | * Maps the wrapped value using the supplied mapper. 158 | * If the wrapped value is null, returns the mapper's 159 | * {@link handleNull} value. 160 | * If the wrapped value is undefined, returns the mapper's 161 | * {@link handleUndefined} value. 162 | * Otherwise, returns the value of the mapper's property whose name matches 163 | * the wrapped value. 164 | * 165 | * @template T - The data type that the enum or string/number literal value 166 | * will be mapped to. 167 | * 168 | * @param mapper - A mapper implementation for type E that returns type T. 169 | * @returns The mapped value from the mapper. 170 | */ 171 | public with(mapper: EnumValueMapperWithNullAndUndefined): T; 172 | } 173 | 174 | /** 175 | * Common implementation for processing an entry of an enum value mapper. 176 | * @param entry - Either the mapped value entry, or {@link unhandledEntry}. 177 | * @param value - The value being mapped. 178 | * @return The provided entry, if it is not an unhandledEntry. 179 | * @throws {Error} If the provided entry is an unhandledEntry. 180 | */ 181 | function processEntry( 182 | entry: T | typeof unhandledEntry, 183 | value: string | number | null | undefined 184 | ): T { 185 | if (entry === unhandledEntry) { 186 | throw createUnhandledEntryError(value); 187 | } else { 188 | return entry; 189 | } 190 | } 191 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/EnumWrapper/object-number.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | // Enum-like object with number values 4 | const TestEnum = { 5 | A: 1, 6 | B: 2, 7 | C: 3 8 | }; 9 | 10 | declare const str: string; 11 | declare const strOrNull: string | null; 12 | declare const strOrUndefined: string | undefined; 13 | 14 | declare const num: number; 15 | declare const numOrNull: number | null; 16 | declare const numOrUndefined: number | undefined; 17 | 18 | declare const key: keyof typeof TestEnum; 19 | declare const keyOrNull: keyof typeof TestEnum | null; 20 | declare const keyOrUndefined: keyof typeof TestEnum | undefined; 21 | 22 | const enumWrapper = $enum(TestEnum); 23 | 24 | // $ExpectType EnumWrapper 25 | enumWrapper; 26 | 27 | // $ExpectType number 28 | enumWrapper.length; 29 | // $ExpectError 30 | enumWrapper.length = 0; // immutable 31 | 32 | // $ExpectType number 33 | enumWrapper.size; 34 | // $ExpectError 35 | enumWrapper.size = 0; // immutable 36 | 37 | // NOTE: Must test via assignability rather than ExpectType because of a change 38 | // in how Readonly tuple types work as of TS 3.1. 39 | // Also cannot test for immutability of items within the entry tuple because of 40 | // this change. 41 | // see: https://github.com/Microsoft/TypeScript/issues/26864 42 | const testEntry: Readonly<["A" | "B" | "C", number]> = enumWrapper[0]; 43 | // $ExpectError 44 | enumWrapper[0] = ["A", TestEnum.A]; // immutable 45 | 46 | // $ExpectType IterableIterator<"A" | "B" | "C"> 47 | enumWrapper.keys(); 48 | 49 | // $ExpectType IterableIterator 50 | enumWrapper.values(); 51 | 52 | // NOTE: Must test via assignability rather than ExpectType because of a change 53 | // in how Readonly tuple types work as of TS 3.1. 54 | // Also cannot test for immutability of items within the iterated entry tuples 55 | // because of this change. 56 | // see: https://github.com/Microsoft/TypeScript/issues/26864 57 | const testEntryIterator: IterableIterator> = enumWrapper.entries(); 60 | for (const entry of enumWrapper.entries()) { 61 | const testIteratedEntry: Readonly<["A" | "B" | "C", number]> = entry; 62 | } 63 | 64 | // $ExpectType void 65 | enumWrapper.forEach((value, key, collection, index) => { 66 | // $ExpectType number 67 | value; 68 | // $ExpectType "A" | "B" | "C" 69 | key; 70 | // $ExpectType EnumWrapper 71 | collection; 72 | // $ExpectType number 73 | index; 74 | 75 | return num; 76 | }); 77 | 78 | // $ExpectType number[] 79 | enumWrapper.map((value, key, collection, index) => { 80 | // $ExpectType number 81 | value; 82 | // $ExpectType "A" | "B" | "C" 83 | key; 84 | // $ExpectType EnumWrapper 85 | collection; 86 | // $ExpectType number 87 | index; 88 | 89 | return num; 90 | }); 91 | 92 | // $ExpectType ("A" | "B" | "C")[] 93 | enumWrapper.getKeys(); 94 | 95 | // $ExpectType number[] 96 | enumWrapper.getValues(); 97 | 98 | // NOTE: Must test via assignability rather than ExpectType because of a change 99 | // in how Readonly tuple types work as of TS 3.1. 100 | // Also cannot test for immutability of items within the entry tuple because of 101 | // this change. 102 | // see: https://github.com/Microsoft/TypeScript/issues/26864 103 | const testEntries: Readonly< 104 | ["A" | "B" | "C", number] 105 | >[] = enumWrapper.getEntries(); 106 | 107 | // $ExpectType boolean 108 | enumWrapper.isKey(str); 109 | // $ExpectType boolean 110 | enumWrapper.isKey(strOrNull); 111 | // $ExpectType boolean 112 | enumWrapper.isKey(strOrUndefined); 113 | 114 | if (enumWrapper.isKey(str)) { 115 | // $ExpectType "A" | "B" | "C" 116 | str; 117 | } 118 | 119 | if (enumWrapper.isKey(strOrNull)) { 120 | // $ExpectType "A" | "B" | "C" 121 | strOrNull; 122 | } 123 | 124 | if (enumWrapper.isKey(strOrUndefined)) { 125 | // $ExpectType "A" | "B" | "C" 126 | strOrUndefined; 127 | } 128 | 129 | // $ExpectType "A" | "B" | "C" 130 | enumWrapper.asKeyOrThrow(str); 131 | // $ExpectType "A" | "B" | "C" 132 | enumWrapper.asKeyOrThrow(strOrNull); 133 | // $ExpectType "A" | "B" | "C" 134 | enumWrapper.asKeyOrThrow(strOrUndefined); 135 | 136 | // $ExpectType "A" | "B" | "C" | undefined 137 | enumWrapper.asKeyOrDefault(str); 138 | // $ExpectType "A" | "B" | "C" | undefined 139 | enumWrapper.asKeyOrDefault(strOrNull); 140 | // $ExpectType "A" | "B" | "C" | undefined 141 | enumWrapper.asKeyOrDefault(strOrUndefined); 142 | // $ExpectType "A" | "B" | "C" | undefined 143 | enumWrapper.asKeyOrDefault(str, undefined); 144 | // $ExpectType "A" | "B" | "C" 145 | enumWrapper.asKeyOrDefault(str, key); 146 | // $ExpectType "A" | "B" | "C" | undefined 147 | enumWrapper.asKeyOrDefault(str, keyOrUndefined); 148 | // $ExpectType string 149 | enumWrapper.asKeyOrDefault(str, str); 150 | // $ExpectType string | undefined 151 | enumWrapper.asKeyOrDefault(str, strOrUndefined); 152 | 153 | // $ExpectType boolean 154 | enumWrapper.isValue(num); 155 | // $ExpectType boolean 156 | enumWrapper.isValue(numOrNull); 157 | // $ExpectType boolean 158 | enumWrapper.isValue(numOrUndefined); 159 | // $ExpectError 160 | enumWrapper.isValue(str); 161 | 162 | if (enumWrapper.isValue(num)) { 163 | // $ExpectType number 164 | num; 165 | } 166 | 167 | if (enumWrapper.isValue(numOrNull)) { 168 | // $ExpectType number 169 | numOrNull; 170 | } 171 | 172 | if (enumWrapper.isValue(numOrUndefined)) { 173 | // $ExpectType number 174 | numOrUndefined; 175 | } 176 | 177 | // $ExpectType number 178 | enumWrapper.asValueOrThrow(num); 179 | // $ExpectType number 180 | enumWrapper.asValueOrThrow(numOrNull); 181 | // $ExpectType number 182 | enumWrapper.asValueOrThrow(numOrUndefined); 183 | // $ExpectError 184 | enumWrapper.asValueOrThrow(str); 185 | 186 | // $ExpectType number | undefined 187 | enumWrapper.asValueOrDefault(num); 188 | // $ExpectType number | undefined 189 | enumWrapper.asValueOrDefault(numOrNull); 190 | // $ExpectType number | undefined 191 | enumWrapper.asValueOrDefault(numOrUndefined); 192 | // $ExpectError 193 | enumWrapper.asValueOrDefault(str); 194 | 195 | // $ExpectType number | undefined 196 | enumWrapper.asValueOrDefault(num, undefined); 197 | // $ExpectType number 198 | enumWrapper.asValueOrDefault(num, num); 199 | // $ExpectType number | undefined 200 | enumWrapper.asValueOrDefault(num, numOrUndefined); 201 | // $ExpectError 202 | enumWrapper.asValueOrDefault(num, str); 203 | 204 | // $ExpectType "A" | "B" | "C" 205 | enumWrapper.getKeyOrThrow(num); 206 | // $ExpectType "A" | "B" | "C" 207 | enumWrapper.getKeyOrThrow(numOrNull); 208 | // $ExpectType "A" | "B" | "C" 209 | enumWrapper.getKeyOrThrow(numOrUndefined); 210 | // $ExpectError 211 | enumWrapper.getKeyOrThrow(str); 212 | 213 | // $ExpectType "A" | "B" | "C" | undefined 214 | enumWrapper.getKeyOrDefault(num); 215 | // $ExpectType "A" | "B" | "C" | undefined 216 | enumWrapper.getKeyOrDefault(numOrNull); 217 | // $ExpectType "A" | "B" | "C" | undefined 218 | enumWrapper.getKeyOrDefault(numOrUndefined); 219 | // $ExpectError 220 | enumWrapper.getKeyOrDefault(str); 221 | 222 | // $ExpectType "A" | "B" | "C" 223 | enumWrapper.getKeyOrDefault(num, key); 224 | // $ExpectType "A" | "B" | "C" | undefined 225 | enumWrapper.getKeyOrDefault(num, keyOrUndefined); 226 | // $ExpectType string 227 | enumWrapper.getKeyOrDefault(num, str); 228 | // $ExpectType string | undefined 229 | enumWrapper.getKeyOrDefault(num, strOrUndefined); 230 | 231 | // $ExpectType number 232 | enumWrapper.getValueOrThrow(key); 233 | // $ExpectType number 234 | enumWrapper.getValueOrThrow(keyOrNull); 235 | // $ExpectType number 236 | enumWrapper.getValueOrThrow(keyOrUndefined); 237 | // $ExpectType number 238 | enumWrapper.getValueOrThrow(str); 239 | // $ExpectType number 240 | enumWrapper.getValueOrThrow(strOrNull); 241 | // $ExpectType number 242 | enumWrapper.getValueOrThrow(strOrUndefined); 243 | 244 | // $ExpectType number | undefined 245 | enumWrapper.getValueOrDefault(str); 246 | // $ExpectType number | undefined 247 | enumWrapper.getValueOrDefault(strOrNull); 248 | // $ExpectType number | undefined 249 | enumWrapper.getValueOrDefault(strOrUndefined); 250 | 251 | // $ExpectType number | undefined 252 | enumWrapper.getValueOrDefault(str, undefined); 253 | // $ExpectType number 254 | enumWrapper.getValueOrDefault(str, num); 255 | // $ExpectType number | undefined 256 | enumWrapper.getValueOrDefault(str, numOrUndefined); 257 | // $ExpectError 258 | enumWrapper.getValueOrDefault(str, str); 259 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/EnumWrapper/object-string.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | // Enum-like object with string values 4 | const TestEnum = { 5 | A: "a", 6 | B: "b", 7 | C: "c" 8 | }; 9 | 10 | declare const str: string; 11 | declare const strOrNull: string | null; 12 | declare const strOrUndefined: string | undefined; 13 | 14 | declare const num: number; 15 | declare const numOrNull: number | null; 16 | declare const numOrUndefined: number | undefined; 17 | 18 | declare const key: keyof typeof TestEnum; 19 | declare const keyOrNull: keyof typeof TestEnum | null; 20 | declare const keyOrUndefined: keyof typeof TestEnum | undefined; 21 | 22 | const enumWrapper = $enum(TestEnum); 23 | 24 | // $ExpectType EnumWrapper 25 | enumWrapper; 26 | 27 | // $ExpectType number 28 | enumWrapper.length; 29 | // $ExpectError 30 | enumWrapper.length = 0; // immutable 31 | 32 | // $ExpectType number 33 | enumWrapper.size; 34 | // $ExpectError 35 | enumWrapper.size = 0; // immutable 36 | 37 | // NOTE: Must test via assignability rather than ExpectType because of a change 38 | // in how Readonly tuple types work as of TS 3.1. 39 | // Also cannot test for immutability of items within the entry tuple because of 40 | // this change. 41 | // see: https://github.com/Microsoft/TypeScript/issues/26864 42 | const testEntry: Readonly<["A" | "B" | "C", string]> = enumWrapper[0]; 43 | // $ExpectError 44 | enumWrapper[0] = ["A", TestEnum.A]; // immutable 45 | 46 | // $ExpectType IterableIterator<"A" | "B" | "C"> 47 | enumWrapper.keys(); 48 | 49 | // $ExpectType IterableIterator 50 | enumWrapper.values(); 51 | 52 | // NOTE: Must test via assignability rather than ExpectType because of a change 53 | // in how Readonly tuple types work as of TS 3.1. 54 | // Also cannot test for immutability of items within the iterated entry tuples 55 | // because of this change. 56 | // see: https://github.com/Microsoft/TypeScript/issues/26864 57 | const testEntryIterator: IterableIterator> = enumWrapper.entries(); 60 | for (const entry of enumWrapper.entries()) { 61 | const testIteratedEntry: Readonly<["A" | "B" | "C", string]> = entry; 62 | } 63 | 64 | // $ExpectType void 65 | enumWrapper.forEach((value, key, collection, index) => { 66 | // $ExpectType string 67 | value; 68 | // $ExpectType "A" | "B" | "C" 69 | key; 70 | // $ExpectType EnumWrapper 71 | collection; 72 | // $ExpectType number 73 | index; 74 | 75 | return num; 76 | }); 77 | 78 | // $ExpectType number[] 79 | enumWrapper.map((value, key, collection, index) => { 80 | // $ExpectType string 81 | value; 82 | // $ExpectType "A" | "B" | "C" 83 | key; 84 | // $ExpectType EnumWrapper 85 | collection; 86 | // $ExpectType number 87 | index; 88 | 89 | return num; 90 | }); 91 | 92 | // $ExpectType ("A" | "B" | "C")[] 93 | enumWrapper.getKeys(); 94 | 95 | // $ExpectType string[] 96 | enumWrapper.getValues(); 97 | 98 | // NOTE: Must test via assignability rather than ExpectType because of a change 99 | // in how Readonly tuple types work as of TS 3.1. 100 | // Also cannot test for immutability of items within the entry tuple because of 101 | // this change. 102 | // see: https://github.com/Microsoft/TypeScript/issues/26864 103 | const testEntries: Readonly< 104 | ["A" | "B" | "C", string] 105 | >[] = enumWrapper.getEntries(); 106 | 107 | // $ExpectType boolean 108 | enumWrapper.isKey(str); 109 | // $ExpectType boolean 110 | enumWrapper.isKey(strOrNull); 111 | // $ExpectType boolean 112 | enumWrapper.isKey(strOrUndefined); 113 | 114 | if (enumWrapper.isKey(str)) { 115 | // $ExpectType "A" | "B" | "C" 116 | str; 117 | } 118 | 119 | if (enumWrapper.isKey(strOrNull)) { 120 | // $ExpectType "A" | "B" | "C" 121 | strOrNull; 122 | } 123 | 124 | if (enumWrapper.isKey(strOrUndefined)) { 125 | // $ExpectType "A" | "B" | "C" 126 | strOrUndefined; 127 | } 128 | 129 | // $ExpectType "A" | "B" | "C" 130 | enumWrapper.asKeyOrThrow(str); 131 | // $ExpectType "A" | "B" | "C" 132 | enumWrapper.asKeyOrThrow(strOrNull); 133 | // $ExpectType "A" | "B" | "C" 134 | enumWrapper.asKeyOrThrow(strOrUndefined); 135 | 136 | // $ExpectType "A" | "B" | "C" | undefined 137 | enumWrapper.asKeyOrDefault(str); 138 | // $ExpectType "A" | "B" | "C" | undefined 139 | enumWrapper.asKeyOrDefault(strOrNull); 140 | // $ExpectType "A" | "B" | "C" | undefined 141 | enumWrapper.asKeyOrDefault(strOrUndefined); 142 | // $ExpectType "A" | "B" | "C" | undefined 143 | enumWrapper.asKeyOrDefault(str, undefined); 144 | // $ExpectType "A" | "B" | "C" 145 | enumWrapper.asKeyOrDefault(str, key); 146 | // $ExpectType "A" | "B" | "C" | undefined 147 | enumWrapper.asKeyOrDefault(str, keyOrUndefined); 148 | // $ExpectType string 149 | enumWrapper.asKeyOrDefault(str, str); 150 | // $ExpectType string | undefined 151 | enumWrapper.asKeyOrDefault(str, strOrUndefined); 152 | 153 | // $ExpectType boolean 154 | enumWrapper.isValue(str); 155 | // $ExpectType boolean 156 | enumWrapper.isValue(strOrNull); 157 | // $ExpectType boolean 158 | enumWrapper.isValue(strOrUndefined); 159 | // $ExpectError 160 | enumWrapper.isValue(num); 161 | 162 | if (enumWrapper.isValue(str)) { 163 | // $ExpectType string 164 | str; 165 | } 166 | 167 | if (enumWrapper.isValue(strOrNull)) { 168 | // $ExpectType string 169 | strOrNull; 170 | } 171 | 172 | if (enumWrapper.isValue(strOrUndefined)) { 173 | // $ExpectType string 174 | strOrUndefined; 175 | } 176 | 177 | // $ExpectType string 178 | enumWrapper.asValueOrThrow(str); 179 | // $ExpectType string 180 | enumWrapper.asValueOrThrow(strOrNull); 181 | // $ExpectType string 182 | enumWrapper.asValueOrThrow(strOrUndefined); 183 | // $ExpectError 184 | enumWrapper.asValueOrThrow(num); 185 | 186 | // $ExpectType string | undefined 187 | enumWrapper.asValueOrDefault(str); 188 | // $ExpectType string | undefined 189 | enumWrapper.asValueOrDefault(strOrNull); 190 | // $ExpectType string | undefined 191 | enumWrapper.asValueOrDefault(strOrUndefined); 192 | // $ExpectError 193 | enumWrapper.asValueOrDefault(num); 194 | 195 | // $ExpectType string | undefined 196 | enumWrapper.asValueOrDefault(str, undefined); 197 | // $ExpectType string 198 | enumWrapper.asValueOrDefault(str, str); 199 | // $ExpectType string | undefined 200 | enumWrapper.asValueOrDefault(str, strOrUndefined); 201 | // $ExpectError 202 | enumWrapper.asValueOrDefault(str, num); 203 | 204 | // $ExpectType "A" | "B" | "C" 205 | enumWrapper.getKeyOrThrow(str); 206 | // $ExpectType "A" | "B" | "C" 207 | enumWrapper.getKeyOrThrow(strOrNull); 208 | // $ExpectType "A" | "B" | "C" 209 | enumWrapper.getKeyOrThrow(strOrUndefined); 210 | // $ExpectError 211 | enumWrapper.getKeyOrThrow(num); 212 | 213 | // $ExpectType "A" | "B" | "C" | undefined 214 | enumWrapper.getKeyOrDefault(str); 215 | // $ExpectType "A" | "B" | "C" | undefined 216 | enumWrapper.getKeyOrDefault(strOrNull); 217 | // $ExpectType "A" | "B" | "C" | undefined 218 | enumWrapper.getKeyOrDefault(strOrUndefined); 219 | // $ExpectError 220 | enumWrapper.getKeyOrDefault(num); 221 | 222 | // $ExpectType "A" | "B" | "C" 223 | enumWrapper.getKeyOrDefault(str, key); 224 | // $ExpectType "A" | "B" | "C" | undefined 225 | enumWrapper.getKeyOrDefault(str, keyOrUndefined); 226 | // $ExpectType string 227 | enumWrapper.getKeyOrDefault(str, str); 228 | // $ExpectType string | undefined 229 | enumWrapper.getKeyOrDefault(str, strOrUndefined); 230 | 231 | // $ExpectType string 232 | enumWrapper.getValueOrThrow(key); 233 | // $ExpectType string 234 | enumWrapper.getValueOrThrow(keyOrNull); 235 | // $ExpectType string 236 | enumWrapper.getValueOrThrow(keyOrUndefined); 237 | // $ExpectType string 238 | enumWrapper.getValueOrThrow(str); 239 | // $ExpectType string 240 | enumWrapper.getValueOrThrow(strOrNull); 241 | // $ExpectType string 242 | enumWrapper.getValueOrThrow(strOrUndefined); 243 | 244 | // $ExpectType string | undefined 245 | enumWrapper.getValueOrDefault(str); 246 | // $ExpectType string | undefined 247 | enumWrapper.getValueOrDefault(strOrNull); 248 | // $ExpectType string | undefined 249 | enumWrapper.getValueOrDefault(strOrUndefined); 250 | 251 | // $ExpectType string | undefined 252 | enumWrapper.getValueOrDefault(str, undefined); 253 | // $ExpectType string 254 | enumWrapper.getValueOrDefault(str, str); 255 | // $ExpectType string | undefined 256 | enumWrapper.getValueOrDefault(str, strOrUndefined); 257 | // $ExpectError 258 | enumWrapper.getValueOrDefault(str, num); 259 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/EnumWrapper/object-mixed.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | // Enum-like object with mix of number and string values 4 | const TestEnum = { 5 | A: 1, 6 | B: 2, 7 | C: "c" 8 | }; 9 | 10 | declare const str: string; 11 | declare const strOrNull: string | null; 12 | declare const strOrUndefined: string | undefined; 13 | 14 | declare const num: number; 15 | declare const numOrNull: number | null; 16 | declare const numOrUndefined: number | undefined; 17 | 18 | declare const numstr: number | string; 19 | declare const numstrOrNull: number | string | null; 20 | declare const numstrOrUndefined: number | string | undefined; 21 | 22 | declare const key: keyof typeof TestEnum; 23 | declare const keyOrNull: keyof typeof TestEnum | null; 24 | declare const keyOrUndefined: keyof typeof TestEnum | undefined; 25 | 26 | const enumWrapper = $enum(TestEnum); 27 | 28 | // $ExpectType EnumWrapper 29 | enumWrapper; 30 | 31 | // $ExpectType number 32 | enumWrapper.length; 33 | // $ExpectError 34 | enumWrapper.length = 0; // immutable 35 | 36 | // $ExpectType number 37 | enumWrapper.size; 38 | // $ExpectError 39 | enumWrapper.size = 0; // immutable 40 | 41 | // NOTE: Must test via assignability rather than ExpectType because of a change 42 | // in how Readonly tuple types work as of TS 3.1. 43 | // Also cannot test for immutability of items within the entry tuple because of 44 | // this change. 45 | // see: https://github.com/Microsoft/TypeScript/issues/26864 46 | const testEntry: Readonly<["A" | "B" | "C", string | number]> = enumWrapper[0]; 47 | // $ExpectError 48 | enumWrapper[0] = ["A", TestEnum.A]; // immutable 49 | 50 | // $ExpectType IterableIterator<"A" | "B" | "C"> 51 | enumWrapper.keys(); 52 | 53 | // $ExpectType IterableIterator 54 | enumWrapper.values(); 55 | 56 | // NOTE: Must test via assignability rather than ExpectType because of a change 57 | // in how Readonly tuple types work as of TS 3.1. 58 | // Also cannot test for immutability of items within the iterated entry tuples 59 | // because of this change. 60 | // see: https://github.com/Microsoft/TypeScript/issues/26864 61 | const testEntryIterator: IterableIterator> = enumWrapper.entries(); 64 | for (const entry of enumWrapper.entries()) { 65 | const testIteratedEntry: Readonly<[ 66 | "A" | "B" | "C", 67 | string | number 68 | ]> = entry; 69 | } 70 | 71 | // $ExpectType void 72 | enumWrapper.forEach((value, key, collection, index) => { 73 | // $ExpectType string | number 74 | value; 75 | // $ExpectType "A" | "B" | "C" 76 | key; 77 | // $ExpectType EnumWrapper 78 | collection; 79 | // $ExpectType number 80 | index; 81 | 82 | return num; 83 | }); 84 | 85 | // $ExpectType number[] 86 | enumWrapper.map((value, key, collection, index) => { 87 | // $ExpectType string | number 88 | value; 89 | // $ExpectType "A" | "B" | "C" 90 | key; 91 | // $ExpectType EnumWrapper 92 | collection; 93 | // $ExpectType number 94 | index; 95 | 96 | return num; 97 | }); 98 | 99 | // $ExpectType ("A" | "B" | "C")[] 100 | enumWrapper.getKeys(); 101 | 102 | // $ExpectType (string | number)[] 103 | enumWrapper.getValues(); 104 | 105 | // NOTE: Must test via assignability rather than ExpectType because of a change 106 | // in how Readonly tuple types work as of TS 3.1. 107 | // Also cannot test for immutability of items within the entry tuple because of 108 | // this change. 109 | // see: https://github.com/Microsoft/TypeScript/issues/26864 110 | const testEntries: Readonly< 111 | ["A" | "B" | "C", string | number] 112 | >[] = enumWrapper.getEntries(); 113 | 114 | // $ExpectType boolean 115 | enumWrapper.isKey(str); 116 | // $ExpectType boolean 117 | enumWrapper.isKey(strOrNull); 118 | // $ExpectType boolean 119 | enumWrapper.isKey(strOrUndefined); 120 | 121 | if (enumWrapper.isKey(str)) { 122 | // $ExpectType "A" | "B" | "C" 123 | str; 124 | } 125 | 126 | if (enumWrapper.isKey(strOrNull)) { 127 | // $ExpectType "A" | "B" | "C" 128 | strOrNull; 129 | } 130 | 131 | if (enumWrapper.isKey(strOrUndefined)) { 132 | // $ExpectType "A" | "B" | "C" 133 | strOrUndefined; 134 | } 135 | 136 | // $ExpectType "A" | "B" | "C" 137 | enumWrapper.asKeyOrThrow(str); 138 | // $ExpectType "A" | "B" | "C" 139 | enumWrapper.asKeyOrThrow(strOrNull); 140 | // $ExpectType "A" | "B" | "C" 141 | enumWrapper.asKeyOrThrow(strOrUndefined); 142 | 143 | // $ExpectType "A" | "B" | "C" | undefined 144 | enumWrapper.asKeyOrDefault(str); 145 | // $ExpectType "A" | "B" | "C" | undefined 146 | enumWrapper.asKeyOrDefault(strOrNull); 147 | // $ExpectType "A" | "B" | "C" | undefined 148 | enumWrapper.asKeyOrDefault(strOrUndefined); 149 | // $ExpectType "A" | "B" | "C" | undefined 150 | enumWrapper.asKeyOrDefault(str, undefined); 151 | // $ExpectType "A" | "B" | "C" 152 | enumWrapper.asKeyOrDefault(str, key); 153 | // $ExpectType "A" | "B" | "C" | undefined 154 | enumWrapper.asKeyOrDefault(str, keyOrUndefined); 155 | // $ExpectType string 156 | enumWrapper.asKeyOrDefault(str, str); 157 | // $ExpectType string | undefined 158 | enumWrapper.asKeyOrDefault(str, strOrUndefined); 159 | 160 | // $ExpectType boolean 161 | enumWrapper.isValue(numstr); 162 | // $ExpectType boolean 163 | enumWrapper.isValue(numstrOrNull); 164 | // $ExpectType boolean 165 | enumWrapper.isValue(numstrOrUndefined); 166 | 167 | if (enumWrapper.isValue(numstr)) { 168 | // $ExpectType string | number 169 | numstr; 170 | } 171 | 172 | if (enumWrapper.isValue(numstrOrNull)) { 173 | // $ExpectType string | number 174 | numstrOrNull; 175 | } 176 | 177 | if (enumWrapper.isValue(numstrOrUndefined)) { 178 | // $ExpectType string | number 179 | numstrOrUndefined; 180 | } 181 | 182 | // $ExpectType string | number 183 | enumWrapper.asValueOrThrow(numstr); 184 | // $ExpectType string | number 185 | enumWrapper.asValueOrThrow(numstrOrNull); 186 | // $ExpectType string | number 187 | enumWrapper.asValueOrThrow(numstrOrUndefined); 188 | 189 | // $ExpectType string | number | undefined 190 | enumWrapper.asValueOrDefault(numstr); 191 | // $ExpectType string | number | undefined 192 | enumWrapper.asValueOrDefault(numstrOrNull); 193 | // $ExpectType string | number | undefined 194 | enumWrapper.asValueOrDefault(numstrOrUndefined); 195 | 196 | // $ExpectType string | number | undefined 197 | enumWrapper.asValueOrDefault(numstr, undefined); 198 | // $ExpectType string | number 199 | enumWrapper.asValueOrDefault(numstr, num); 200 | // $ExpectType string | number 201 | enumWrapper.asValueOrDefault(numstr, str); 202 | // $ExpectType string | number 203 | enumWrapper.asValueOrDefault(numstr, numstr); 204 | // $ExpectType string | number | undefined 205 | enumWrapper.asValueOrDefault(num, numstrOrUndefined); 206 | 207 | // $ExpectType "A" | "B" | "C" 208 | enumWrapper.getKeyOrThrow(numstr); 209 | // $ExpectType "A" | "B" | "C" 210 | enumWrapper.getKeyOrThrow(numstrOrNull); 211 | // $ExpectType "A" | "B" | "C" 212 | enumWrapper.getKeyOrThrow(numstrOrUndefined); 213 | 214 | // $ExpectType "A" | "B" | "C" | undefined 215 | enumWrapper.getKeyOrDefault(numstr); 216 | // $ExpectType "A" | "B" | "C" | undefined 217 | enumWrapper.getKeyOrDefault(numstrOrNull); 218 | // $ExpectType "A" | "B" | "C" | undefined 219 | enumWrapper.getKeyOrDefault(numstrOrUndefined); 220 | 221 | // $ExpectType "A" | "B" | "C" 222 | enumWrapper.getKeyOrDefault(numstr, key); 223 | // $ExpectType "A" | "B" | "C" | undefined 224 | enumWrapper.getKeyOrDefault(numstr, keyOrUndefined); 225 | // $ExpectType string 226 | enumWrapper.getKeyOrDefault(numstr, str); 227 | // $ExpectType string | undefined 228 | enumWrapper.getKeyOrDefault(numstr, strOrUndefined); 229 | 230 | // $ExpectType string | number 231 | enumWrapper.getValueOrThrow(key); 232 | // $ExpectType string | number 233 | enumWrapper.getValueOrThrow(keyOrNull); 234 | // $ExpectType string | number 235 | enumWrapper.getValueOrThrow(keyOrUndefined); 236 | // $ExpectType string | number 237 | enumWrapper.getValueOrThrow(str); 238 | // $ExpectType string | number 239 | enumWrapper.getValueOrThrow(strOrNull); 240 | // $ExpectType string | number 241 | enumWrapper.getValueOrThrow(strOrUndefined); 242 | 243 | // $ExpectType string | number | undefined 244 | enumWrapper.getValueOrDefault(str); 245 | // $ExpectType string | number | undefined 246 | enumWrapper.getValueOrDefault(strOrNull); 247 | // $ExpectType string | number | undefined 248 | enumWrapper.getValueOrDefault(strOrUndefined); 249 | 250 | // $ExpectType string | number | undefined 251 | enumWrapper.getValueOrDefault(str, undefined); 252 | // $ExpectType string | number 253 | enumWrapper.getValueOrDefault(str, num); 254 | // $ExpectType string | number 255 | enumWrapper.getValueOrDefault(str, str); 256 | // $ExpectType string | number 257 | enumWrapper.getValueOrDefault(str, numstr); 258 | // $ExpectType string | number | undefined 259 | enumWrapper.getValueOrDefault(str, numstrOrUndefined); 260 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/EnumWrapper/enum-number.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | // Enum with number values 4 | enum TestEnum { 5 | A, 6 | B, 7 | C 8 | } 9 | 10 | declare const str: string; 11 | declare const strOrNull: string | null; 12 | declare const strOrUndefined: string | undefined; 13 | 14 | declare const num: number; 15 | declare const numOrNull: number | null; 16 | declare const numOrUndefined: number | undefined; 17 | 18 | declare const key: keyof typeof TestEnum; 19 | declare const keyOrNull: keyof typeof TestEnum | null; 20 | declare const keyOrUndefined: keyof typeof TestEnum | undefined; 21 | 22 | declare const value: TestEnum; 23 | declare const valueOrNull: TestEnum | null; 24 | declare const valueOrUndefined: TestEnum | undefined; 25 | 26 | const enumWrapper = $enum(TestEnum); 27 | 28 | // $ExpectType EnumWrapper 29 | enumWrapper; 30 | 31 | // $ExpectType number 32 | enumWrapper.length; 33 | // $ExpectError 34 | enumWrapper.length = 0; // immutable 35 | 36 | // $ExpectType number 37 | enumWrapper.size; 38 | // $ExpectError 39 | enumWrapper.size = 0; // immutable 40 | 41 | // NOTE: Must test via assignability rather than ExpectType because of a change 42 | // in how Readonly tuple types work as of TS 3.1. 43 | // Also cannot test for immutability of items within the entry tuple because of 44 | // this change. 45 | // see: https://github.com/Microsoft/TypeScript/issues/26864 46 | const testEntry: Readonly<["A" | "B" | "C", TestEnum]> = enumWrapper[0]; 47 | // $ExpectError 48 | enumWrapper[0] = ["A", TestEnum.A]; // immutable 49 | 50 | // $ExpectType IterableIterator<"A" | "B" | "C"> 51 | enumWrapper.keys(); 52 | 53 | // $ExpectType IterableIterator 54 | enumWrapper.values(); 55 | 56 | // NOTE: Must test via assignability rather than ExpectType because of a change 57 | // in how Readonly tuple types work as of TS 3.1. 58 | // Also cannot test for immutability of items within the iterated entry tuples 59 | // because of this change. 60 | // see: https://github.com/Microsoft/TypeScript/issues/26864 61 | const testEntryIterator: IterableIterator> = enumWrapper.entries(); 64 | for (const entry of enumWrapper.entries()) { 65 | const testIteratedEntry: Readonly<["A" | "B" | "C", TestEnum]> = entry; 66 | } 67 | 68 | // $ExpectType void 69 | enumWrapper.forEach((value, key, collection, index) => { 70 | // $ExpectType TestEnum 71 | value; 72 | // $ExpectType "A" | "B" | "C" 73 | key; 74 | // $ExpectType EnumWrapper 75 | collection; 76 | // $ExpectType number 77 | index; 78 | 79 | return num; 80 | }); 81 | 82 | // $ExpectType number[] 83 | enumWrapper.map((value, key, collection, index) => { 84 | // $ExpectType TestEnum 85 | value; 86 | // $ExpectType "A" | "B" | "C" 87 | key; 88 | // $ExpectType EnumWrapper 89 | collection; 90 | // $ExpectType number 91 | index; 92 | 93 | return num; 94 | }); 95 | 96 | // $ExpectType ("A" | "B" | "C")[] 97 | enumWrapper.getKeys(); 98 | 99 | // $ExpectType TestEnum[] 100 | enumWrapper.getValues(); 101 | 102 | // NOTE: Must test via assignability rather than ExpectType because of a change 103 | // in how Readonly tuple types work as of TS 3.1. 104 | // Also cannot test for immutability of items within the entry tuple because of 105 | // this change. 106 | // see: https://github.com/Microsoft/TypeScript/issues/26864 107 | const testEntries: Readonly< 108 | ["A" | "B" | "C", TestEnum] 109 | >[] = enumWrapper.getEntries(); 110 | 111 | // $ExpectType boolean 112 | enumWrapper.isKey(str); 113 | // $ExpectType boolean 114 | enumWrapper.isKey(strOrNull); 115 | // $ExpectType boolean 116 | enumWrapper.isKey(strOrUndefined); 117 | 118 | if (enumWrapper.isKey(str)) { 119 | // $ExpectType "A" | "B" | "C" 120 | str; 121 | } 122 | 123 | if (enumWrapper.isKey(strOrNull)) { 124 | // $ExpectType "A" | "B" | "C" 125 | strOrNull; 126 | } 127 | 128 | if (enumWrapper.isKey(strOrUndefined)) { 129 | // $ExpectType "A" | "B" | "C" 130 | strOrUndefined; 131 | } 132 | 133 | // $ExpectType "A" | "B" | "C" 134 | enumWrapper.asKeyOrThrow(str); 135 | // $ExpectType "A" | "B" | "C" 136 | enumWrapper.asKeyOrThrow(strOrNull); 137 | // $ExpectType "A" | "B" | "C" 138 | enumWrapper.asKeyOrThrow(strOrUndefined); 139 | 140 | // $ExpectType "A" | "B" | "C" | undefined 141 | enumWrapper.asKeyOrDefault(str); 142 | // $ExpectType "A" | "B" | "C" | undefined 143 | enumWrapper.asKeyOrDefault(strOrNull); 144 | // $ExpectType "A" | "B" | "C" | undefined 145 | enumWrapper.asKeyOrDefault(strOrUndefined); 146 | // $ExpectType "A" | "B" | "C" | undefined 147 | enumWrapper.asKeyOrDefault(str, undefined); 148 | // $ExpectType "A" | "B" | "C" 149 | enumWrapper.asKeyOrDefault(str, key); 150 | // $ExpectType "A" | "B" | "C" | undefined 151 | enumWrapper.asKeyOrDefault(str, keyOrUndefined); 152 | // $ExpectType string 153 | enumWrapper.asKeyOrDefault(str, str); 154 | // $ExpectType string | undefined 155 | enumWrapper.asKeyOrDefault(str, strOrUndefined); 156 | 157 | // $ExpectType boolean 158 | enumWrapper.isValue(num); 159 | // $ExpectType boolean 160 | enumWrapper.isValue(numOrNull); 161 | // $ExpectType boolean 162 | enumWrapper.isValue(numOrUndefined); 163 | // $ExpectError 164 | enumWrapper.isValue(str); 165 | 166 | if (enumWrapper.isValue(num)) { 167 | // $ExpectType TestEnum 168 | num; 169 | } 170 | 171 | if (enumWrapper.isValue(numOrNull)) { 172 | // $ExpectType TestEnum 173 | numOrNull; 174 | } 175 | 176 | if (enumWrapper.isValue(numOrUndefined)) { 177 | // $ExpectType TestEnum 178 | numOrUndefined; 179 | } 180 | 181 | // $ExpectType TestEnum 182 | enumWrapper.asValueOrThrow(num); 183 | // $ExpectType TestEnum 184 | enumWrapper.asValueOrThrow(numOrNull); 185 | // $ExpectType TestEnum 186 | enumWrapper.asValueOrThrow(numOrUndefined); 187 | // $ExpectError 188 | enumWrapper.asValueOrThrow(str); 189 | 190 | // $ExpectType TestEnum | undefined 191 | enumWrapper.asValueOrDefault(num); 192 | // $ExpectType TestEnum | undefined 193 | enumWrapper.asValueOrDefault(numOrNull); 194 | // $ExpectType TestEnum | undefined 195 | enumWrapper.asValueOrDefault(numOrUndefined); 196 | // $ExpectError 197 | enumWrapper.asValueOrDefault(str); 198 | 199 | // $ExpectType TestEnum | undefined 200 | enumWrapper.asValueOrDefault(num, undefined); 201 | // $ExpectType TestEnum 202 | enumWrapper.asValueOrDefault(num, value); 203 | // $ExpectType TestEnum | undefined 204 | enumWrapper.asValueOrDefault(num, valueOrUndefined); 205 | // $ExpectType number 206 | enumWrapper.asValueOrDefault(num, num); 207 | // $ExpectType number | undefined 208 | enumWrapper.asValueOrDefault(num, numOrUndefined); 209 | // $ExpectError 210 | enumWrapper.asValueOrDefault(num, str); 211 | 212 | // $ExpectType "A" | "B" | "C" 213 | enumWrapper.getKeyOrThrow(num); 214 | // $ExpectType "A" | "B" | "C" 215 | enumWrapper.getKeyOrThrow(numOrNull); 216 | // $ExpectType "A" | "B" | "C" 217 | enumWrapper.getKeyOrThrow(numOrUndefined); 218 | // $ExpectError 219 | enumWrapper.getKeyOrThrow(str); 220 | 221 | // $ExpectType "A" | "B" | "C" | undefined 222 | enumWrapper.getKeyOrDefault(num); 223 | // $ExpectType "A" | "B" | "C" | undefined 224 | enumWrapper.getKeyOrDefault(numOrNull); 225 | // $ExpectType "A" | "B" | "C" | undefined 226 | enumWrapper.getKeyOrDefault(numOrUndefined); 227 | // $ExpectError 228 | enumWrapper.getKeyOrDefault(str); 229 | 230 | // $ExpectType "A" | "B" | "C" 231 | enumWrapper.getKeyOrDefault(num, key); 232 | // $ExpectType "A" | "B" | "C" | undefined 233 | enumWrapper.getKeyOrDefault(num, keyOrUndefined); 234 | // $ExpectType string 235 | enumWrapper.getKeyOrDefault(num, str); 236 | // $ExpectType string | undefined 237 | enumWrapper.getKeyOrDefault(num, strOrUndefined); 238 | 239 | // $ExpectType TestEnum 240 | enumWrapper.getValueOrThrow(key); 241 | // $ExpectType TestEnum 242 | enumWrapper.getValueOrThrow(keyOrNull); 243 | // $ExpectType TestEnum 244 | enumWrapper.getValueOrThrow(keyOrUndefined); 245 | // $ExpectType TestEnum 246 | enumWrapper.getValueOrThrow(str); 247 | // $ExpectType TestEnum 248 | enumWrapper.getValueOrThrow(strOrNull); 249 | // $ExpectType TestEnum 250 | enumWrapper.getValueOrThrow(strOrUndefined); 251 | 252 | // $ExpectType TestEnum | undefined 253 | enumWrapper.getValueOrDefault(str); 254 | // $ExpectType TestEnum | undefined 255 | enumWrapper.getValueOrDefault(strOrNull); 256 | // $ExpectType TestEnum | undefined 257 | enumWrapper.getValueOrDefault(strOrUndefined); 258 | 259 | // $ExpectType TestEnum | undefined 260 | enumWrapper.getValueOrDefault(str, undefined); 261 | // $ExpectType TestEnum 262 | enumWrapper.getValueOrDefault(str, value); 263 | // $ExpectType TestEnum | undefined 264 | enumWrapper.getValueOrDefault(str, valueOrUndefined); 265 | // $ExpectType number 266 | enumWrapper.getValueOrDefault(str, num); 267 | // $ExpectType number | undefined 268 | enumWrapper.getValueOrDefault(str, numOrUndefined); 269 | // $ExpectError 270 | enumWrapper.getValueOrDefault(str, str); 271 | 272 | // $ExpectType Pick 273 | enumWrapper.createEnumSubset("A", "C"); 274 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/EnumWrapper/enum-string.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | // Enum with string values 4 | enum TestEnum { 5 | A = "a", 6 | B = "b", 7 | C = "c" 8 | } 9 | 10 | declare const str: string; 11 | declare const strOrNull: string | null; 12 | declare const strOrUndefined: string | undefined; 13 | 14 | declare const num: number; 15 | declare const numOrNull: number | null; 16 | declare const numOrUndefined: number | undefined; 17 | 18 | declare const key: keyof typeof TestEnum; 19 | declare const keyOrNull: keyof typeof TestEnum | null; 20 | declare const keyOrUndefined: keyof typeof TestEnum | undefined; 21 | 22 | declare const value: TestEnum; 23 | declare const valueOrNull: TestEnum | null; 24 | declare const valueOrUndefined: TestEnum | undefined; 25 | 26 | const enumWrapper = $enum(TestEnum); 27 | 28 | // $ExpectType EnumWrapper 29 | enumWrapper; 30 | 31 | // $ExpectType number 32 | enumWrapper.length; 33 | // $ExpectError 34 | enumWrapper.length = 0; // immutable 35 | 36 | // $ExpectType number 37 | enumWrapper.size; 38 | // $ExpectError 39 | enumWrapper.size = 0; // immutable 40 | 41 | // NOTE: Must test via assignability rather than ExpectType because of a change 42 | // in how Readonly tuple types work as of TS 3.1. 43 | // Also cannot test for immutability of items within the entry tuple because of 44 | // this change. 45 | // see: https://github.com/Microsoft/TypeScript/issues/26864 46 | const testEntry: Readonly<["A" | "B" | "C", TestEnum]> = enumWrapper[0]; 47 | // $ExpectError 48 | enumWrapper[0] = ["A", TestEnum.A]; // immutable 49 | 50 | // $ExpectType IterableIterator<"A" | "B" | "C"> 51 | enumWrapper.keys(); 52 | 53 | // $ExpectType IterableIterator 54 | enumWrapper.values(); 55 | 56 | // NOTE: Must test via assignability rather than ExpectType because of a change 57 | // in how Readonly tuple types work as of TS 3.1. 58 | // Also cannot test for immutability of items within the iterated entry tuples 59 | // because of this change. 60 | // see: https://github.com/Microsoft/TypeScript/issues/26864 61 | const testEntryIterator: IterableIterator> = enumWrapper.entries(); 64 | for (const entry of enumWrapper.entries()) { 65 | const testIteratedEntry: Readonly<["A" | "B" | "C", TestEnum]> = entry; 66 | } 67 | 68 | // $ExpectType void 69 | enumWrapper.forEach((value, key, collection, index) => { 70 | // $ExpectType TestEnum 71 | value; 72 | // $ExpectType "A" | "B" | "C" 73 | key; 74 | // $ExpectType EnumWrapper 75 | collection; 76 | // $ExpectType number 77 | index; 78 | 79 | return num; 80 | }); 81 | 82 | // $ExpectType number[] 83 | enumWrapper.map((value, key, collection, index) => { 84 | // $ExpectType TestEnum 85 | value; 86 | // $ExpectType "A" | "B" | "C" 87 | key; 88 | // $ExpectType EnumWrapper 89 | collection; 90 | // $ExpectType number 91 | index; 92 | 93 | return num; 94 | }); 95 | 96 | // $ExpectType ("A" | "B" | "C")[] 97 | enumWrapper.getKeys(); 98 | 99 | // $ExpectType TestEnum[] 100 | enumWrapper.getValues(); 101 | 102 | // NOTE: Must test via assignability rather than ExpectType because of a change 103 | // in how Readonly tuple types work as of TS 3.1. 104 | // Also cannot test for immutability of items within the entry tuple because of 105 | // this change. 106 | // see: https://github.com/Microsoft/TypeScript/issues/26864 107 | const testEntries: Readonly< 108 | ["A" | "B" | "C", TestEnum] 109 | >[] = enumWrapper.getEntries(); 110 | 111 | // $ExpectType boolean 112 | enumWrapper.isKey(str); 113 | // $ExpectType boolean 114 | enumWrapper.isKey(strOrNull); 115 | // $ExpectType boolean 116 | enumWrapper.isKey(strOrUndefined); 117 | 118 | if (enumWrapper.isKey(str)) { 119 | // $ExpectType "A" | "B" | "C" 120 | str; 121 | } 122 | 123 | if (enumWrapper.isKey(strOrNull)) { 124 | // $ExpectType "A" | "B" | "C" 125 | strOrNull; 126 | } 127 | 128 | if (enumWrapper.isKey(strOrUndefined)) { 129 | // $ExpectType "A" | "B" | "C" 130 | strOrUndefined; 131 | } 132 | 133 | // $ExpectType "A" | "B" | "C" 134 | enumWrapper.asKeyOrThrow(str); 135 | // $ExpectType "A" | "B" | "C" 136 | enumWrapper.asKeyOrThrow(strOrNull); 137 | // $ExpectType "A" | "B" | "C" 138 | enumWrapper.asKeyOrThrow(strOrUndefined); 139 | 140 | // $ExpectType "A" | "B" | "C" | undefined 141 | enumWrapper.asKeyOrDefault(str); 142 | // $ExpectType "A" | "B" | "C" | undefined 143 | enumWrapper.asKeyOrDefault(strOrNull); 144 | // $ExpectType "A" | "B" | "C" | undefined 145 | enumWrapper.asKeyOrDefault(strOrUndefined); 146 | // $ExpectType "A" | "B" | "C" | undefined 147 | enumWrapper.asKeyOrDefault(str, undefined); 148 | // $ExpectType "A" | "B" | "C" 149 | enumWrapper.asKeyOrDefault(str, key); 150 | // $ExpectType "A" | "B" | "C" | undefined 151 | enumWrapper.asKeyOrDefault(str, keyOrUndefined); 152 | // $ExpectType string 153 | enumWrapper.asKeyOrDefault(str, str); 154 | // $ExpectType string | undefined 155 | enumWrapper.asKeyOrDefault(str, strOrUndefined); 156 | 157 | // $ExpectType boolean 158 | enumWrapper.isValue(str); 159 | // $ExpectType boolean 160 | enumWrapper.isValue(strOrNull); 161 | // $ExpectType boolean 162 | enumWrapper.isValue(strOrUndefined); 163 | // $ExpectError 164 | enumWrapper.isValue(num); 165 | 166 | if (enumWrapper.isValue(str)) { 167 | // $ExpectType TestEnum 168 | str; 169 | } 170 | 171 | if (enumWrapper.isValue(strOrNull)) { 172 | // $ExpectType TestEnum 173 | strOrNull; 174 | } 175 | 176 | if (enumWrapper.isValue(strOrUndefined)) { 177 | // $ExpectType TestEnum 178 | strOrUndefined; 179 | } 180 | 181 | // $ExpectType TestEnum 182 | enumWrapper.asValueOrThrow(str); 183 | // $ExpectType TestEnum 184 | enumWrapper.asValueOrThrow(strOrNull); 185 | // $ExpectType TestEnum 186 | enumWrapper.asValueOrThrow(strOrUndefined); 187 | // $ExpectError 188 | enumWrapper.asValueOrThrow(num); 189 | 190 | // $ExpectType TestEnum | undefined 191 | enumWrapper.asValueOrDefault(str); 192 | // $ExpectType TestEnum | undefined 193 | enumWrapper.asValueOrDefault(strOrNull); 194 | // $ExpectType TestEnum | undefined 195 | enumWrapper.asValueOrDefault(strOrUndefined); 196 | // $ExpectError 197 | enumWrapper.asValueOrDefault(num); 198 | 199 | // $ExpectType TestEnum | undefined 200 | enumWrapper.asValueOrDefault(str, undefined); 201 | // $ExpectType TestEnum 202 | enumWrapper.asValueOrDefault(str, value); 203 | // $ExpectType TestEnum | undefined 204 | enumWrapper.asValueOrDefault(str, valueOrUndefined); 205 | // $ExpectType string 206 | enumWrapper.asValueOrDefault(str, str); 207 | // $ExpectType string | undefined 208 | enumWrapper.asValueOrDefault(str, strOrUndefined); 209 | // $ExpectError 210 | enumWrapper.asValueOrDefault(str, num); 211 | 212 | // $ExpectType "A" | "B" | "C" 213 | enumWrapper.getKeyOrThrow(str); 214 | // $ExpectType "A" | "B" | "C" 215 | enumWrapper.getKeyOrThrow(strOrNull); 216 | // $ExpectType "A" | "B" | "C" 217 | enumWrapper.getKeyOrThrow(strOrUndefined); 218 | // $ExpectError 219 | enumWrapper.getKeyOrThrow(num); 220 | 221 | // $ExpectType "A" | "B" | "C" | undefined 222 | enumWrapper.getKeyOrDefault(str); 223 | // $ExpectType "A" | "B" | "C" | undefined 224 | enumWrapper.getKeyOrDefault(strOrNull); 225 | // $ExpectType "A" | "B" | "C" | undefined 226 | enumWrapper.getKeyOrDefault(strOrUndefined); 227 | // $ExpectError 228 | enumWrapper.getKeyOrDefault(num); 229 | 230 | // $ExpectType "A" | "B" | "C" 231 | enumWrapper.getKeyOrDefault(str, key); 232 | // $ExpectType "A" | "B" | "C" | undefined 233 | enumWrapper.getKeyOrDefault(str, keyOrUndefined); 234 | // $ExpectType string 235 | enumWrapper.getKeyOrDefault(str, str); 236 | // $ExpectType string | undefined 237 | enumWrapper.getKeyOrDefault(str, strOrUndefined); 238 | 239 | // $ExpectType TestEnum 240 | enumWrapper.getValueOrThrow(key); 241 | // $ExpectType TestEnum 242 | enumWrapper.getValueOrThrow(keyOrNull); 243 | // $ExpectType TestEnum 244 | enumWrapper.getValueOrThrow(keyOrUndefined); 245 | // $ExpectType TestEnum 246 | enumWrapper.getValueOrThrow(str); 247 | // $ExpectType TestEnum 248 | enumWrapper.getValueOrThrow(strOrNull); 249 | // $ExpectType TestEnum 250 | enumWrapper.getValueOrThrow(strOrUndefined); 251 | 252 | // $ExpectType TestEnum | undefined 253 | enumWrapper.getValueOrDefault(str); 254 | // $ExpectType TestEnum | undefined 255 | enumWrapper.getValueOrDefault(strOrNull); 256 | // $ExpectType TestEnum | undefined 257 | enumWrapper.getValueOrDefault(strOrUndefined); 258 | 259 | // $ExpectType TestEnum | undefined 260 | enumWrapper.getValueOrDefault(str, undefined); 261 | // $ExpectType TestEnum 262 | enumWrapper.getValueOrDefault(str, value); 263 | // $ExpectType TestEnum | undefined 264 | enumWrapper.getValueOrDefault(str, valueOrUndefined); 265 | // $ExpectType string 266 | enumWrapper.getValueOrDefault(str, str); 267 | // $ExpectType string | undefined 268 | enumWrapper.getValueOrDefault(str, strOrUndefined); 269 | // $ExpectError 270 | enumWrapper.getValueOrDefault(str, num); 271 | 272 | // $ExpectType Pick 273 | enumWrapper.createEnumSubset("A", "C"); 274 | -------------------------------------------------------------------------------- /type_tests/v2_9_plus/EnumWrapper/enum-mixed.dtslint.ts: -------------------------------------------------------------------------------- 1 | import { $enum } from "ts-enum-util"; 2 | 3 | // Enum with mix of number and string values 4 | enum TestEnum { 5 | A, 6 | B, 7 | C = "c" 8 | } 9 | 10 | declare const str: string; 11 | declare const strOrNull: string | null; 12 | declare const strOrUndefined: string | undefined; 13 | 14 | declare const num: number; 15 | declare const numOrNull: number | null; 16 | declare const numOrUndefined: number | undefined; 17 | 18 | declare const numstr: number | string; 19 | declare const numstrOrNull: number | string | null; 20 | declare const numstrOrUndefined: number | string | undefined; 21 | 22 | declare const key: keyof typeof TestEnum; 23 | declare const keyOrNull: keyof typeof TestEnum | null; 24 | declare const keyOrUndefined: keyof typeof TestEnum | undefined; 25 | 26 | declare const value: TestEnum; 27 | declare const valueOrNull: TestEnum | null; 28 | declare const valueOrUndefined: TestEnum | undefined; 29 | 30 | const enumWrapper = $enum(TestEnum); 31 | 32 | // $ExpectType EnumWrapper 33 | enumWrapper; 34 | 35 | // $ExpectType number 36 | enumWrapper.length; 37 | // $ExpectError 38 | enumWrapper.length = 0; // immutable 39 | 40 | // $ExpectType number 41 | enumWrapper.size; 42 | // $ExpectError 43 | enumWrapper.size = 0; // immutable 44 | 45 | // NOTE: Must test via assignability rather than ExpectType because of a change 46 | // in how Readonly tuple types work as of TS 3.1. 47 | // Also cannot test for immutability of items within the entry tuple because of 48 | // this change. 49 | // see: https://github.com/Microsoft/TypeScript/issues/26864 50 | const testEntry: Readonly<["A" | "B" | "C", TestEnum]> = enumWrapper[0]; 51 | // $ExpectError 52 | enumWrapper[0] = ["A", TestEnum.A]; // immutable 53 | 54 | // $ExpectType IterableIterator<"A" | "B" | "C"> 55 | enumWrapper.keys(); 56 | 57 | // $ExpectType IterableIterator 58 | enumWrapper.values(); 59 | 60 | // NOTE: Must test via assignability rather than ExpectType because of a change 61 | // in how Readonly tuple types work as of TS 3.1. 62 | // Also cannot test for immutability of items within the iterated entry tuples 63 | // because of this change. 64 | // see: https://github.com/Microsoft/TypeScript/issues/26864 65 | const testEntryIterator: IterableIterator> = enumWrapper.entries(); 68 | for (const entry of enumWrapper.entries()) { 69 | const testIteratedEntry: Readonly<["A" | "B" | "C", TestEnum]> = entry; 70 | } 71 | 72 | // $ExpectType void 73 | enumWrapper.forEach((value, key, collection, index) => { 74 | // $ExpectType TestEnum 75 | value; 76 | // $ExpectType "A" | "B" | "C" 77 | key; 78 | // $ExpectType EnumWrapper 79 | collection; 80 | // $ExpectType number 81 | index; 82 | 83 | return num; 84 | }); 85 | 86 | // $ExpectType number[] 87 | enumWrapper.map((value, key, collection, index) => { 88 | // $ExpectType TestEnum 89 | value; 90 | // $ExpectType "A" | "B" | "C" 91 | key; 92 | // $ExpectType EnumWrapper 93 | collection; 94 | // $ExpectType number 95 | index; 96 | 97 | return num; 98 | }); 99 | 100 | // $ExpectType ("A" | "B" | "C")[] 101 | enumWrapper.getKeys(); 102 | 103 | // $ExpectType TestEnum[] 104 | enumWrapper.getValues(); 105 | 106 | // NOTE: Must test via assignability rather than ExpectType because of a change 107 | // in how Readonly tuple types work as of TS 3.1. 108 | // Also cannot test for immutability of items within the entry tuple because of 109 | // this change. 110 | // see: https://github.com/Microsoft/TypeScript/issues/26864 111 | const testEntries: Readonly< 112 | ["A" | "B" | "C", TestEnum] 113 | >[] = enumWrapper.getEntries(); 114 | 115 | // $ExpectType boolean 116 | enumWrapper.isKey(str); 117 | // $ExpectType boolean 118 | enumWrapper.isKey(strOrNull); 119 | // $ExpectType boolean 120 | enumWrapper.isKey(strOrUndefined); 121 | 122 | if (enumWrapper.isKey(str)) { 123 | // $ExpectType "A" | "B" | "C" 124 | str; 125 | } 126 | 127 | if (enumWrapper.isKey(strOrNull)) { 128 | // $ExpectType "A" | "B" | "C" 129 | strOrNull; 130 | } 131 | 132 | if (enumWrapper.isKey(strOrUndefined)) { 133 | // $ExpectType "A" | "B" | "C" 134 | strOrUndefined; 135 | } 136 | 137 | // $ExpectType "A" | "B" | "C" 138 | enumWrapper.asKeyOrThrow(str); 139 | // $ExpectType "A" | "B" | "C" 140 | enumWrapper.asKeyOrThrow(strOrNull); 141 | // $ExpectType "A" | "B" | "C" 142 | enumWrapper.asKeyOrThrow(strOrUndefined); 143 | 144 | // $ExpectType "A" | "B" | "C" | undefined 145 | enumWrapper.asKeyOrDefault(str); 146 | // $ExpectType "A" | "B" | "C" | undefined 147 | enumWrapper.asKeyOrDefault(strOrNull); 148 | // $ExpectType "A" | "B" | "C" | undefined 149 | enumWrapper.asKeyOrDefault(strOrUndefined); 150 | // $ExpectType "A" | "B" | "C" | undefined 151 | enumWrapper.asKeyOrDefault(str, undefined); 152 | // $ExpectType "A" | "B" | "C" 153 | enumWrapper.asKeyOrDefault(str, key); 154 | // $ExpectType "A" | "B" | "C" | undefined 155 | enumWrapper.asKeyOrDefault(str, keyOrUndefined); 156 | // $ExpectType string 157 | enumWrapper.asKeyOrDefault(str, str); 158 | // $ExpectType string | undefined 159 | enumWrapper.asKeyOrDefault(str, strOrUndefined); 160 | 161 | // $ExpectType boolean 162 | enumWrapper.isValue(numstr); 163 | // $ExpectType boolean 164 | enumWrapper.isValue(numstrOrNull); 165 | // $ExpectType boolean 166 | enumWrapper.isValue(numstrOrUndefined); 167 | 168 | if (enumWrapper.isValue(numstr)) { 169 | // $ExpectType TestEnum 170 | numstr; 171 | } 172 | 173 | if (enumWrapper.isValue(numstrOrNull)) { 174 | // $ExpectType TestEnum 175 | numstrOrNull; 176 | } 177 | 178 | if (enumWrapper.isValue(numstrOrUndefined)) { 179 | // $ExpectType TestEnum 180 | numstrOrUndefined; 181 | } 182 | 183 | // $ExpectType TestEnum 184 | enumWrapper.asValueOrThrow(numstr); 185 | // $ExpectType TestEnum 186 | enumWrapper.asValueOrThrow(numstrOrNull); 187 | // $ExpectType TestEnum 188 | enumWrapper.asValueOrThrow(numstrOrUndefined); 189 | 190 | // $ExpectType TestEnum | undefined 191 | enumWrapper.asValueOrDefault(numstr); 192 | // $ExpectType TestEnum | undefined 193 | enumWrapper.asValueOrDefault(numstrOrNull); 194 | // $ExpectType TestEnum | undefined 195 | enumWrapper.asValueOrDefault(numstrOrUndefined); 196 | 197 | // $ExpectType TestEnum | undefined 198 | enumWrapper.asValueOrDefault(numstr, undefined); 199 | // $ExpectType TestEnum 200 | enumWrapper.asValueOrDefault(numstr, value); 201 | // $ExpectType TestEnum | undefined 202 | enumWrapper.asValueOrDefault(numstr, valueOrUndefined); 203 | // $ExpectType string | number 204 | enumWrapper.asValueOrDefault(numstr, num); 205 | // $ExpectType string | number 206 | enumWrapper.asValueOrDefault(numstr, str); 207 | // $ExpectType string | number 208 | enumWrapper.asValueOrDefault(numstr, numstr); 209 | // $ExpectType string | number | undefined 210 | enumWrapper.asValueOrDefault(num, numstrOrUndefined); 211 | 212 | // $ExpectType "A" | "B" | "C" 213 | enumWrapper.getKeyOrThrow(numstr); 214 | // $ExpectType "A" | "B" | "C" 215 | enumWrapper.getKeyOrThrow(numstrOrNull); 216 | // $ExpectType "A" | "B" | "C" 217 | enumWrapper.getKeyOrThrow(numstrOrUndefined); 218 | 219 | // $ExpectType "A" | "B" | "C" | undefined 220 | enumWrapper.getKeyOrDefault(numstr); 221 | // $ExpectType "A" | "B" | "C" | undefined 222 | enumWrapper.getKeyOrDefault(numstrOrNull); 223 | // $ExpectType "A" | "B" | "C" | undefined 224 | enumWrapper.getKeyOrDefault(numstrOrUndefined); 225 | 226 | // $ExpectType "A" | "B" | "C" 227 | enumWrapper.getKeyOrDefault(numstr, key); 228 | // $ExpectType "A" | "B" | "C" | undefined 229 | enumWrapper.getKeyOrDefault(numstr, keyOrUndefined); 230 | // $ExpectType string 231 | enumWrapper.getKeyOrDefault(numstr, str); 232 | // $ExpectType string | undefined 233 | enumWrapper.getKeyOrDefault(numstr, strOrUndefined); 234 | 235 | // $ExpectType TestEnum 236 | enumWrapper.getValueOrThrow(key); 237 | // $ExpectType TestEnum 238 | enumWrapper.getValueOrThrow(keyOrNull); 239 | // $ExpectType TestEnum 240 | enumWrapper.getValueOrThrow(keyOrUndefined); 241 | // $ExpectType TestEnum 242 | enumWrapper.getValueOrThrow(str); 243 | // $ExpectType TestEnum 244 | enumWrapper.getValueOrThrow(strOrNull); 245 | // $ExpectType TestEnum 246 | enumWrapper.getValueOrThrow(strOrUndefined); 247 | 248 | // $ExpectType TestEnum | undefined 249 | enumWrapper.getValueOrDefault(str); 250 | // $ExpectType TestEnum | undefined 251 | enumWrapper.getValueOrDefault(strOrNull); 252 | // $ExpectType TestEnum | undefined 253 | enumWrapper.getValueOrDefault(strOrUndefined); 254 | 255 | // $ExpectType TestEnum | undefined 256 | enumWrapper.getValueOrDefault(str, undefined); 257 | // $ExpectType TestEnum 258 | enumWrapper.getValueOrDefault(str, value); 259 | // $ExpectType TestEnum | undefined 260 | enumWrapper.getValueOrDefault(str, valueOrUndefined); 261 | // $ExpectType string | number 262 | enumWrapper.getValueOrDefault(str, num); 263 | // $ExpectType string | number 264 | enumWrapper.getValueOrDefault(str, str); 265 | // $ExpectType string | number 266 | enumWrapper.getValueOrDefault(str, numstr); 267 | // $ExpectType string | number | undefined 268 | enumWrapper.getValueOrDefault(str, numstrOrUndefined); 269 | 270 | // $ExpectType Pick 271 | enumWrapper.createEnumSubset("A", "C"); 272 | -------------------------------------------------------------------------------- /tests/mapValue-mixed.test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | $enum, 3 | EnumValueMapper, 4 | EnumValueMapperWithNull, 5 | EnumValueMapperWithUndefined, 6 | EnumValueMapperWithNullAndUndefined 7 | } from "../src"; 8 | 9 | enum RGB { 10 | R = "r", 11 | G = 1, 12 | B = "b" 13 | } 14 | 15 | describe("mapValue (string/number mix)", () => { 16 | describe("Without null/undefined", () => { 17 | interface TestEntry { 18 | isUnexpected?: boolean; 19 | value: RGB; 20 | result: string; 21 | } 22 | 23 | const TEST_ENTRIES: TestEntry[] = [ 24 | { 25 | value: RGB.R, 26 | result: "Red!" 27 | }, 28 | { 29 | value: RGB.G, 30 | result: "Green!" 31 | }, 32 | { 33 | value: RGB.B, 34 | result: "Blue!" 35 | }, 36 | { 37 | isUnexpected: true, 38 | value: (null as any) as RGB, 39 | result: "Unexpected!" 40 | }, 41 | { 42 | isUnexpected: true, 43 | value: (undefined as any) as RGB, 44 | result: "Unexpected!" 45 | }, 46 | { 47 | isUnexpected: true, 48 | value: ("unexpected!" as any) as RGB, 49 | result: "Unexpected!" 50 | }, 51 | { 52 | isUnexpected: true, 53 | // matches a standard property name on Object.prototype 54 | value: ("toString" as any) as RGB, 55 | result: "Unexpected!" 56 | } 57 | ]; 58 | 59 | const mappers: EnumValueMapper[] = [ 60 | { 61 | [RGB.R]: "Red!", 62 | [RGB.G]: "Green!", 63 | [RGB.B]: "Blue!" 64 | }, 65 | { 66 | [RGB.R]: "Red!", 67 | [RGB.G]: "Green!", 68 | [RGB.B]: "Blue!", 69 | [$enum.handleUnexpected]: "Unexpected!" 70 | }, 71 | { 72 | [RGB.R]: $enum.unhandledEntry, 73 | [RGB.G]: $enum.unhandledEntry, 74 | [RGB.B]: $enum.unhandledEntry, 75 | [$enum.handleUnexpected]: $enum.unhandledEntry 76 | } 77 | ]; 78 | 79 | for (const mapper of mappers) { 80 | for (const testEntry of TEST_ENTRIES) { 81 | if (mapper[RGB.R] === $enum.unhandledEntry) { 82 | test(`Unhandled entry throws error (${testEntry.value}`, () => { 83 | expect(() => { 84 | $enum.mapValue(testEntry.value).with(mapper); 85 | }).toThrowError(`Unhandled value: ${testEntry.value}`); 86 | }); 87 | } else if ( 88 | mapper.hasOwnProperty($enum.handleUnexpected) || 89 | !testEntry.isUnexpected 90 | ) { 91 | test(`Correct value is returned (${testEntry.value})`, () => { 92 | const result = $enum 93 | .mapValue(testEntry.value) 94 | .with(mapper); 95 | 96 | expect(result).toBe(testEntry.result); 97 | }); 98 | } else { 99 | test(`Unhandled unexpected value throws error (${testEntry.value})`, () => { 100 | expect(() => { 101 | $enum.mapValue(testEntry.value).with(mapper); 102 | }).toThrowError(`Unexpected value: ${testEntry.value}`); 103 | }); 104 | } 105 | } 106 | } 107 | }); 108 | 109 | describe("With null", () => { 110 | interface TestEntry { 111 | isUnexpected?: boolean; 112 | value: RGB | null; 113 | result: string; 114 | } 115 | 116 | const TEST_ENTRIES: TestEntry[] = [ 117 | { 118 | value: RGB.R, 119 | result: "Red!" 120 | }, 121 | { 122 | value: RGB.G, 123 | result: "Green!" 124 | }, 125 | { 126 | value: RGB.B, 127 | result: "Blue!" 128 | }, 129 | { 130 | value: null, 131 | result: "Null!" 132 | }, 133 | { 134 | isUnexpected: true, 135 | value: (undefined as any) as RGB, 136 | result: "Unexpected!" 137 | }, 138 | { 139 | isUnexpected: true, 140 | value: ("unexpected!" as any) as RGB, 141 | result: "Unexpected!" 142 | }, 143 | { 144 | isUnexpected: true, 145 | // matches a standard property name on Object.prototype 146 | value: ("toString" as any) as RGB, 147 | result: "Unexpected!" 148 | } 149 | ]; 150 | 151 | const mappers: EnumValueMapperWithNull[] = [ 152 | { 153 | [RGB.R]: "Red!", 154 | [RGB.G]: "Green!", 155 | [RGB.B]: "Blue!", 156 | [$enum.handleNull]: "Null!" 157 | }, 158 | { 159 | [RGB.R]: "Red!", 160 | [RGB.G]: "Green!", 161 | [RGB.B]: "Blue!", 162 | [$enum.handleNull]: "Null!", 163 | [$enum.handleUnexpected]: "Unexpected!" 164 | }, 165 | { 166 | [RGB.R]: $enum.unhandledEntry, 167 | [RGB.G]: $enum.unhandledEntry, 168 | [RGB.B]: $enum.unhandledEntry, 169 | [$enum.handleNull]: $enum.unhandledEntry, 170 | [$enum.handleUnexpected]: $enum.unhandledEntry 171 | } 172 | ]; 173 | 174 | for (const mapper of mappers) { 175 | for (const testEntry of TEST_ENTRIES) { 176 | if (mapper[RGB.R] === $enum.unhandledEntry) { 177 | test(`Unhandled entry throws error (${testEntry.value}`, () => { 178 | expect(() => { 179 | $enum.mapValue(testEntry.value).with(mapper); 180 | }).toThrowError(`Unhandled value: ${testEntry.value}`); 181 | }); 182 | } else if ( 183 | mapper.hasOwnProperty($enum.handleUnexpected) || 184 | !testEntry.isUnexpected 185 | ) { 186 | test(`Correct value is returned (${testEntry.value})`, () => { 187 | const result = $enum 188 | .mapValue(testEntry.value) 189 | .with(mapper); 190 | 191 | expect(result).toBe(testEntry.result); 192 | }); 193 | } else { 194 | test(`Unhandled unexpected value throws error (${testEntry.value})`, () => { 195 | expect(() => { 196 | $enum.mapValue(testEntry.value).with(mapper); 197 | }).toThrowError(`Unexpected value: ${testEntry.value}`); 198 | }); 199 | } 200 | } 201 | } 202 | }); 203 | 204 | describe("With undefined", () => { 205 | interface TestEntry { 206 | isUnexpected?: boolean; 207 | value: RGB | undefined; 208 | result: string; 209 | } 210 | 211 | const TEST_ENTRIES: TestEntry[] = [ 212 | { 213 | value: RGB.R, 214 | result: "Red!" 215 | }, 216 | { 217 | value: RGB.G, 218 | result: "Green!" 219 | }, 220 | { 221 | value: RGB.B, 222 | result: "Blue!" 223 | }, 224 | { 225 | value: undefined, 226 | result: "Undefined!" 227 | }, 228 | { 229 | isUnexpected: true, 230 | value: (null as any) as RGB, 231 | result: "Unexpected!" 232 | }, 233 | { 234 | isUnexpected: true, 235 | value: ("unexpected!" as any) as RGB, 236 | result: "Unexpected!" 237 | }, 238 | { 239 | isUnexpected: true, 240 | // matches a standard property name on Object.prototype 241 | value: ("toString" as any) as RGB, 242 | result: "Unexpected!" 243 | } 244 | ]; 245 | 246 | const mappers: EnumValueMapperWithUndefined[] = [ 247 | { 248 | [RGB.R]: "Red!", 249 | [RGB.G]: "Green!", 250 | [RGB.B]: "Blue!", 251 | [$enum.handleUndefined]: "Undefined!" 252 | }, 253 | { 254 | [RGB.R]: "Red!", 255 | [RGB.G]: "Green!", 256 | [RGB.B]: "Blue!", 257 | [$enum.handleUndefined]: "Undefined!", 258 | [$enum.handleUnexpected]: "Unexpected!" 259 | }, 260 | { 261 | [RGB.R]: $enum.unhandledEntry, 262 | [RGB.G]: $enum.unhandledEntry, 263 | [RGB.B]: $enum.unhandledEntry, 264 | [$enum.handleUndefined]: $enum.unhandledEntry, 265 | [$enum.handleUnexpected]: $enum.unhandledEntry 266 | } 267 | ]; 268 | 269 | for (const mapper of mappers) { 270 | for (const testEntry of TEST_ENTRIES) { 271 | if (mapper[RGB.R] === $enum.unhandledEntry) { 272 | test(`Unhandled entry throws error (${testEntry.value}`, () => { 273 | expect(() => { 274 | $enum.mapValue(testEntry.value).with(mapper); 275 | }).toThrowError(`Unhandled value: ${testEntry.value}`); 276 | }); 277 | } else if ( 278 | mapper.hasOwnProperty($enum.handleUnexpected) || 279 | !testEntry.isUnexpected 280 | ) { 281 | test(`Correct value is returned (${testEntry.value})`, () => { 282 | const result = $enum 283 | .mapValue(testEntry.value) 284 | .with(mapper); 285 | 286 | expect(result).toBe(testEntry.result); 287 | }); 288 | } else { 289 | test(`Unhandled unexpected value throws error (${testEntry.value})`, () => { 290 | expect(() => { 291 | $enum.mapValue(testEntry.value).with(mapper); 292 | }).toThrowError(`Unexpected value: ${testEntry.value}`); 293 | }); 294 | } 295 | } 296 | } 297 | }); 298 | 299 | describe("With null and undefined", () => { 300 | interface TestEntry { 301 | isUnexpected?: boolean; 302 | value: RGB | null | undefined; 303 | result: string; 304 | } 305 | 306 | const TEST_ENTRIES: TestEntry[] = [ 307 | { 308 | value: RGB.R, 309 | result: "Red!" 310 | }, 311 | { 312 | value: RGB.G, 313 | result: "Green!" 314 | }, 315 | { 316 | value: RGB.B, 317 | result: "Blue!" 318 | }, 319 | { 320 | value: null, 321 | result: "Null!" 322 | }, 323 | { 324 | value: undefined, 325 | result: "Undefined!" 326 | }, 327 | { 328 | isUnexpected: true, 329 | value: ("unexpected!" as any) as RGB, 330 | result: "Unexpected!" 331 | }, 332 | { 333 | isUnexpected: true, 334 | // matches a standard property name on Object.prototype 335 | value: ("toString" as any) as RGB, 336 | result: "Unexpected!" 337 | } 338 | ]; 339 | 340 | const mappers: EnumValueMapperWithNullAndUndefined[] = [ 341 | { 342 | [RGB.R]: "Red!", 343 | [RGB.G]: "Green!", 344 | [RGB.B]: "Blue!", 345 | [$enum.handleNull]: "Null!", 346 | [$enum.handleUndefined]: "Undefined!" 347 | }, 348 | { 349 | [RGB.R]: "Red!", 350 | [RGB.G]: "Green!", 351 | [RGB.B]: "Blue!", 352 | [$enum.handleNull]: "Null!", 353 | [$enum.handleUndefined]: "Undefined!", 354 | [$enum.handleUnexpected]: "Unexpected!" 355 | }, 356 | { 357 | [RGB.R]: $enum.unhandledEntry, 358 | [RGB.G]: $enum.unhandledEntry, 359 | [RGB.B]: $enum.unhandledEntry, 360 | [$enum.handleNull]: $enum.unhandledEntry, 361 | [$enum.handleUndefined]: $enum.unhandledEntry, 362 | [$enum.handleUnexpected]: $enum.unhandledEntry 363 | } 364 | ]; 365 | 366 | for (const mapper of mappers) { 367 | for (const testEntry of TEST_ENTRIES) { 368 | if (mapper[RGB.R] === $enum.unhandledEntry) { 369 | test(`Unhandled entry throws error (${testEntry.value}`, () => { 370 | expect(() => { 371 | $enum.mapValue(testEntry.value).with(mapper); 372 | }).toThrowError(`Unhandled value: ${testEntry.value}`); 373 | }); 374 | } else if ( 375 | mapper.hasOwnProperty($enum.handleUnexpected) || 376 | !testEntry.isUnexpected 377 | ) { 378 | test(`Correct value is returned (${testEntry.value})`, () => { 379 | const result = $enum 380 | .mapValue(testEntry.value) 381 | .with(mapper); 382 | 383 | expect(result).toBe(testEntry.result); 384 | }); 385 | } else { 386 | test(`Unhandled unexpected value throws error (${testEntry.value})`, () => { 387 | expect(() => { 388 | $enum.mapValue(testEntry.value).with(mapper); 389 | }).toThrowError(`Unexpected value: ${testEntry.value}`); 390 | }); 391 | } 392 | } 393 | } 394 | }); 395 | }); 396 | --------------------------------------------------------------------------------