ts-safe-enum 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -38,30 +38,40 @@ bun add ts-safe-enum
38
38
  ```typescript
39
39
  import { defineEnum, type InferValue } from 'ts-safe-enum';
40
40
 
41
+ // Object form — when keys and values differ:
41
42
  const Status = defineEnum({
42
43
  Active: 'active',
43
44
  Inactive: 'inactive',
44
45
  Pending: 'pending',
45
46
  });
46
47
 
47
- // Extract the union type — no `as const` needed
48
48
  type Status = InferValue<typeof Status>;
49
49
  // 'active' | 'inactive' | 'pending'
50
+
51
+ // Array form — shorthand when key and value are the same:
52
+ const Direction = defineEnum(['Up', 'Down', 'Left', 'Right']);
53
+
54
+ type Direction = InferValue<typeof Direction>;
55
+ // 'Up' | 'Down' | 'Left' | 'Right'
50
56
  ```
51
57
 
52
58
  ## API
53
59
 
54
60
  ### `defineEnum(definition)` — Create a type-safe enum
55
61
 
56
- Accepts a plain object. Literal types are inferred automatically.
62
+ Accepts a plain object or an array of strings. Literal types are inferred automatically.
57
63
 
58
64
  ```typescript
59
- const Direction = defineEnum({
60
- Up: 'up',
61
- Down: 'down',
62
- Left: 'left',
63
- Right: 'right',
65
+ // Object map keys to custom values:
66
+ const HttpStatus = defineEnum({
67
+ Ok: 200,
68
+ NotFound: 404,
69
+ ServerError: 500,
64
70
  });
71
+
72
+ // Array — shorthand when key === value:
73
+ const Role = defineEnum(['Admin', 'Editor', 'Viewer']);
74
+ // Equivalent to: { Admin: 'Admin', Editor: 'Editor', Viewer: 'Viewer' }
65
75
  ```
66
76
 
67
77
  ---
package/dist/index.cjs CHANGED
@@ -1,2 +1,2 @@
1
- 'use strict';function l(n){let s=Object.values(n),u=Object.keys(n),a=Object.entries(n),t=new Set(Object.values(n)),o=new Map;for(let[e,r]of Object.entries(n))o.set(r,e);return {definition:n,values(){return s},keys(){return u},entries(){return a},is(e){return t.has(e)},parse(e){if(t.has(e))return {ok:true,value:e};let r=s.map(String).join(", ");return {ok:false,error:`Invalid enum value: "${String(e)}". Expected one of: ${r}`}},keyOf(e){return o.get(e)}}}exports.defineEnum=l;//# sourceMappingURL=index.cjs.map
1
+ 'use strict';function u(e){let r=Object.values(e),s=Object.keys(e),y=Object.entries(e),o=new Set(Object.values(e)),a=new Map;for(let[n,t]of Object.entries(e))a.set(t,n);return {definition:e,values(){return r},keys(){return s},entries(){return y},is(n){return o.has(n)},parse(n){if(o.has(n))return {ok:true,value:n};let t=r.map(String).join(", ");return {ok:false,error:`Invalid enum value: "${String(n)}". Expected one of: ${t}`}},keyOf(n){return a.get(n)}}}function T(e){if(Array.isArray(e)){let r={};for(let s of e)r[s]=s;return u(r)}return u(e)}exports.defineEnum=T;//# sourceMappingURL=index.cjs.map
2
2
  //# sourceMappingURL=index.cjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["defineEnum","definition","allValues","allKeys","allEntries","valueSet","reverseLookup","key","value","validValues"],"mappings":"aAiFO,SAASA,CAAAA,CACdC,CAAAA,CACiB,CACjB,IAAMC,CAAAA,CAAY,MAAA,CAAO,MAAA,CAAOD,CAAU,CAAA,CACpCE,CAAAA,CAAU,MAAA,CAAO,IAAA,CAAKF,CAAU,EAChCG,CAAAA,CAAa,MAAA,CAAO,OAAA,CAAQH,CAAU,CAAA,CAEtCI,CAAAA,CAAW,IAAI,GAAA,CAAqB,MAAA,CAAO,MAAA,CAAOJ,CAAU,CAAC,CAAA,CAC7DK,CAAAA,CAAgB,IAAI,GAAA,CAE1B,IAAA,GAAW,CAACC,CAAAA,CAAKC,CAAK,CAAA,GAAK,MAAA,CAAO,OAAA,CAAQP,CAAU,CAAA,CAClDK,CAAAA,CAAc,GAAA,CAAIE,CAAAA,CAAOD,CAAG,CAAA,CAG9B,OAAO,CACL,UAAA,CAAAN,CAAAA,CAEA,MAAA,EAAS,CACP,OAAOC,CACT,CAAA,CAEA,IAAA,EAAO,CACL,OAAOC,CACT,CAAA,CAEA,OAAA,EAAU,CACR,OAAOC,CACT,CAAA,CAEA,EAAA,CAAGI,CAAAA,CAAqC,CACtC,OAAOH,CAAAA,CAAS,GAAA,CAAIG,CAAwB,CAC9C,CAAA,CAEA,KAAA,CAAMA,CAAAA,CAAyC,CAC7C,GAAIH,CAAAA,CAAS,GAAA,CAAIG,CAAwB,CAAA,CACvC,OAAO,CAAE,EAAA,CAAI,IAAA,CAAM,KAAA,CAAOA,CAAoB,CAAA,CAGhD,IAAMC,CAAAA,CAAcP,CAAAA,CAAU,IAAI,MAAM,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA,CACnD,OAAO,CACL,EAAA,CAAI,KAAA,CACJ,KAAA,CAAO,CAAA,qBAAA,EAAwB,MAAA,CAAOM,CAAK,CAAC,CAAA,oBAAA,EAAuBC,CAAW,CAAA,CAChF,CACF,CAAA,CAEA,KAAA,CAAMD,CAAAA,CAAmB,CACvB,OAAOF,CAAAA,CAAc,GAAA,CAAIE,CAAwB,CACnD,CACF,CACF","file":"index.cjs","sourcesContent":["/**\n * The type of values in an enum definition.\n * Extracted as a union of all values.\n */\nexport type InferValue<T extends EnumInstance<Record<string, string | number>>> =\n ReturnType<T[\"values\"]>[number];\n\n/**\n * The type of keys in an enum definition.\n * Extracted as a union of all keys.\n */\nexport type InferKey<T extends EnumInstance<Record<string, string | number>>> =\n ReturnType<T[\"keys\"]>[number];\n\n/** The result of `.parse()` — compatible with `ts-safe-result`'s Result shape. */\nexport type ParseResult<Value> =\n | { readonly ok: true; readonly value: Value }\n | { readonly ok: false; readonly error: string };\n\nexport interface EnumInstance<T extends Record<string, string | number>> {\n /** The original definition object. */\n readonly definition: T;\n\n /** Get all values as a readonly array. */\n values(): readonly T[keyof T][];\n\n /** Get all keys as a readonly array. */\n keys(): readonly (keyof T)[];\n\n /** Get all entries as readonly [key, value] pairs. */\n entries(): readonly (readonly [keyof T, T[keyof T]])[];\n\n /**\n * Type guard — check if an unknown value is a valid enum value.\n *\n * @example\n * if (Status.is(input)) {\n * // input is narrowed to 'active' | 'inactive' | 'pending'\n * }\n */\n is(value: unknown): value is T[keyof T];\n\n /**\n * Parse an unknown value into a typed enum value.\n * Returns `{ ok: true, value }` or `{ ok: false, error }`.\n *\n * The shape is compatible with `ts-safe-result` — you can wrap it\n * with `ok()`/`err()` for full chaining support.\n */\n parse(value: unknown): ParseResult<T[keyof T]>;\n\n /**\n * Get the key corresponding to a given value.\n * Returns undefined if the value is not in the enum.\n *\n * @example\n * Status.keyOf('active') // 'Active'\n */\n keyOf(value: T[keyof T]): keyof T | undefined;\n}\n\n/**\n * Define a type-safe enum from a plain object.\n * No `as const` needed — literal types are inferred automatically.\n *\n * @example\n * const Status = defineEnum({\n * Active: 'active',\n * Inactive: 'inactive',\n * Pending: 'pending',\n * });\n *\n * type Status = InferValue<typeof Status>;\n * // 'active' | 'inactive' | 'pending'\n *\n * Status.values() // ['active', 'inactive', 'pending']\n * Status.keys() // ['Active', 'Inactive', 'Pending']\n * Status.is('active') // true (type guard)\n * Status.parse('xxx') // { ok: false, error: '...' }\n * Status.keyOf('active') // 'Active'\n */\nexport function defineEnum<const T extends Record<string, string | number>>(\n definition: T,\n): EnumInstance<T> {\n const allValues = Object.values(definition) as unknown as T[keyof T][];\n const allKeys = Object.keys(definition) as unknown as (keyof T)[];\n const allEntries = Object.entries(definition) as unknown as (readonly [keyof T, T[keyof T]])[];\n\n const valueSet = new Set<string | number>(Object.values(definition));\n const reverseLookup = new Map<string | number, string>();\n\n for (const [key, value] of Object.entries(definition)) {\n reverseLookup.set(value, key);\n }\n\n return {\n definition,\n\n values() {\n return allValues;\n },\n\n keys() {\n return allKeys;\n },\n\n entries() {\n return allEntries;\n },\n\n is(value: unknown): value is T[keyof T] {\n return valueSet.has(value as string | number);\n },\n\n parse(value: unknown): ParseResult<T[keyof T]> {\n if (valueSet.has(value as string | number)) {\n return { ok: true, value: value as T[keyof T] };\n }\n\n const validValues = allValues.map(String).join(\", \");\n return {\n ok: false,\n error: `Invalid enum value: \"${String(value)}\". Expected one of: ${validValues}`,\n };\n },\n\n keyOf(value: T[keyof T]) {\n return reverseLookup.get(value as string | number) as keyof T | undefined;\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":["createInstance","definition","allValues","allKeys","allEntries","valueSet","reverseLookup","key","value","validValues","defineEnum","input","member"],"mappings":"aAkEA,SAASA,CAAAA,CACPC,CAAAA,CACiB,CACjB,IAAMC,EAAY,MAAA,CAAO,MAAA,CAAOD,CAAU,CAAA,CACpCE,EAAU,MAAA,CAAO,IAAA,CAAKF,CAAU,CAAA,CAChCG,EAAa,MAAA,CAAO,OAAA,CAAQH,CAAU,CAAA,CAEtCI,CAAAA,CAAW,IAAI,GAAA,CAAqB,MAAA,CAAO,OAAOJ,CAAU,CAAC,CAAA,CAC7DK,CAAAA,CAAgB,IAAI,GAAA,CAE1B,IAAA,GAAW,CAACC,CAAAA,CAAKC,CAAK,CAAA,GAAK,MAAA,CAAO,OAAA,CAAQP,CAAU,CAAA,CAClDK,CAAAA,CAAc,GAAA,CAAIE,CAAAA,CAAOD,CAAG,CAAA,CAG9B,OAAO,CACL,UAAA,CAAAN,EAEA,MAAA,EAAS,CACP,OAAOC,CACT,EAEA,IAAA,EAAO,CACL,OAAOC,CACT,EAEA,OAAA,EAAU,CACR,OAAOC,CACT,EAEA,EAAA,CAAGI,CAAAA,CAAqC,CACtC,OAAOH,EAAS,GAAA,CAAIG,CAAwB,CAC9C,CAAA,CAEA,MAAMA,CAAAA,CAAyC,CAC7C,GAAIH,CAAAA,CAAS,GAAA,CAAIG,CAAwB,CAAA,CACvC,OAAO,CAAE,EAAA,CAAI,IAAA,CAAM,KAAA,CAAOA,CAAoB,EAGhD,IAAMC,CAAAA,CAAcP,CAAAA,CAAU,GAAA,CAAI,MAAM,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA,CACnD,OAAO,CACL,EAAA,CAAI,KAAA,CACJ,MAAO,CAAA,qBAAA,EAAwB,MAAA,CAAOM,CAAK,CAAC,uBAAuBC,CAAW,CAAA,CAChF,CACF,CAAA,CAEA,MAAMD,CAAAA,CAAmB,CACvB,OAAOF,CAAAA,CAAc,GAAA,CAAIE,CAAwB,CACnD,CACF,CACF,CA2BO,SAASE,CAAAA,CAAWC,CAAAA,CAA4D,CACrF,GAAI,KAAA,CAAM,OAAA,CAAQA,CAAK,EAAG,CACxB,IAAMV,CAAAA,CAAqC,GAC3C,IAAA,IAAWW,CAAAA,IAAUD,CAAAA,CACnBV,CAAAA,CAAWW,CAAM,CAAA,CAAIA,CAAAA,CAEvB,OAAOZ,CAAAA,CAAeC,CAAU,CAClC,CAEA,OAAOD,CAAAA,CAAeW,CAAwC,CAChE","file":"index.cjs","sourcesContent":["/**\n * The type of values in an enum definition.\n * Extracted as a union of all values.\n */\nexport type InferValue<T extends EnumInstance<Record<string, string | number>>> =\n ReturnType<T[\"values\"]>[number];\n\n/**\n * The type of keys in an enum definition.\n * Extracted as a union of all keys.\n */\nexport type InferKey<T extends EnumInstance<Record<string, string | number>>> =\n ReturnType<T[\"keys\"]>[number];\n\n/** The result of `.parse()` — compatible with `ts-safe-result`'s Result shape. */\nexport type ParseResult<Value> =\n | { readonly ok: true; readonly value: Value }\n | { readonly ok: false; readonly error: string };\n\n/** Converts an array of string literals into a `{ Key: 'Key' }` record type. */\ntype ArrayToEnum<T extends readonly string[]> = {\n readonly [K in T[number]]: K;\n};\n\nexport interface EnumInstance<T extends Record<string, string | number>> {\n /** The original definition object. */\n readonly definition: T;\n\n /** Get all values as a readonly array. */\n values(): readonly T[keyof T][];\n\n /** Get all keys as a readonly array. */\n keys(): readonly (keyof T)[];\n\n /** Get all entries as readonly [key, value] pairs. */\n entries(): readonly (readonly [keyof T, T[keyof T]])[];\n\n /**\n * Type guard — check if an unknown value is a valid enum value.\n *\n * @example\n * if (Status.is(input)) {\n * // input is narrowed to 'active' | 'inactive' | 'pending'\n * }\n */\n is(value: unknown): value is T[keyof T];\n\n /**\n * Parse an unknown value into a typed enum value.\n * Returns `{ ok: true, value }` or `{ ok: false, error }`.\n *\n * The shape is compatible with `ts-safe-result` — you can wrap it\n * with `ok()`/`err()` for full chaining support.\n */\n parse(value: unknown): ParseResult<T[keyof T]>;\n\n /**\n * Get the key corresponding to a given value.\n * Returns undefined if the value is not in the enum.\n *\n * @example\n * Status.keyOf('active') // 'Active'\n */\n keyOf(value: T[keyof T]): keyof T | undefined;\n}\n\nfunction createInstance<T extends Record<string, string | number>>(\n definition: T,\n): EnumInstance<T> {\n const allValues = Object.values(definition) as unknown as T[keyof T][];\n const allKeys = Object.keys(definition) as unknown as (keyof T)[];\n const allEntries = Object.entries(definition) as unknown as (readonly [keyof T, T[keyof T]])[];\n\n const valueSet = new Set<string | number>(Object.values(definition));\n const reverseLookup = new Map<string | number, string>();\n\n for (const [key, value] of Object.entries(definition)) {\n reverseLookup.set(value, key);\n }\n\n return {\n definition,\n\n values() {\n return allValues;\n },\n\n keys() {\n return allKeys;\n },\n\n entries() {\n return allEntries;\n },\n\n is(value: unknown): value is T[keyof T] {\n return valueSet.has(value as string | number);\n },\n\n parse(value: unknown): ParseResult<T[keyof T]> {\n if (valueSet.has(value as string | number)) {\n return { ok: true, value: value as T[keyof T] };\n }\n\n const validValues = allValues.map(String).join(\", \");\n return {\n ok: false,\n error: `Invalid enum value: \"${String(value)}\". Expected one of: ${validValues}`,\n };\n },\n\n keyOf(value: T[keyof T]) {\n return reverseLookup.get(value as string | number) as keyof T | undefined;\n },\n };\n}\n\n/**\n * Define a type-safe enum from a plain object or an array of strings.\n *\n * @example\n * // Object form — explicit key-value mapping:\n * const Status = defineEnum({\n * Active: 'active',\n * Inactive: 'inactive',\n * Pending: 'pending',\n * });\n * type Status = InferValue<typeof Status>;\n * // 'active' | 'inactive' | 'pending'\n *\n * @example\n * // Array form — shorthand when key and value are the same:\n * const Direction = defineEnum(['Up', 'Down', 'Left', 'Right']);\n * type Direction = InferValue<typeof Direction>;\n * // 'Up' | 'Down' | 'Left' | 'Right'\n */\nexport function defineEnum<const T extends readonly string[]>(\n members: T,\n): EnumInstance<ArrayToEnum<T>>;\nexport function defineEnum<const T extends Record<string, string | number>>(\n definition: T,\n): EnumInstance<T>;\nexport function defineEnum(input: readonly string[] | Record<string, string | number>) {\n if (Array.isArray(input)) {\n const definition: Record<string, string> = {};\n for (const member of input as readonly string[]) {\n definition[member] = member;\n }\n return createInstance(definition) as EnumInstance<any>;\n }\n\n return createInstance(input as Record<string, string | number>) as EnumInstance<any>;\n}\n"]}
package/dist/index.d.cts CHANGED
@@ -16,6 +16,10 @@ type ParseResult<Value> = {
16
16
  readonly ok: false;
17
17
  readonly error: string;
18
18
  };
19
+ /** Converts an array of string literals into a `{ Key: 'Key' }` record type. */
20
+ type ArrayToEnum<T extends readonly string[]> = {
21
+ readonly [K in T[number]]: K;
22
+ };
19
23
  interface EnumInstance<T extends Record<string, string | number>> {
20
24
  /** The original definition object. */
21
25
  readonly definition: T;
@@ -52,25 +56,25 @@ interface EnumInstance<T extends Record<string, string | number>> {
52
56
  keyOf(value: T[keyof T]): keyof T | undefined;
53
57
  }
54
58
  /**
55
- * Define a type-safe enum from a plain object.
56
- * No `as const` needed — literal types are inferred automatically.
59
+ * Define a type-safe enum from a plain object or an array of strings.
57
60
  *
58
61
  * @example
62
+ * // Object form — explicit key-value mapping:
59
63
  * const Status = defineEnum({
60
64
  * Active: 'active',
61
65
  * Inactive: 'inactive',
62
66
  * Pending: 'pending',
63
67
  * });
64
- *
65
68
  * type Status = InferValue<typeof Status>;
66
69
  * // 'active' | 'inactive' | 'pending'
67
70
  *
68
- * Status.values() // ['active', 'inactive', 'pending']
69
- * Status.keys() // ['Active', 'Inactive', 'Pending']
70
- * Status.is('active') // true (type guard)
71
- * Status.parse('xxx') // { ok: false, error: '...' }
72
- * Status.keyOf('active') // 'Active'
71
+ * @example
72
+ * // Array form — shorthand when key and value are the same:
73
+ * const Direction = defineEnum(['Up', 'Down', 'Left', 'Right']);
74
+ * type Direction = InferValue<typeof Direction>;
75
+ * // 'Up' | 'Down' | 'Left' | 'Right'
73
76
  */
77
+ declare function defineEnum<const T extends readonly string[]>(members: T): EnumInstance<ArrayToEnum<T>>;
74
78
  declare function defineEnum<const T extends Record<string, string | number>>(definition: T): EnumInstance<T>;
75
79
 
76
80
  export { type EnumInstance, type InferKey, type InferValue, type ParseResult, defineEnum };
package/dist/index.d.ts CHANGED
@@ -16,6 +16,10 @@ type ParseResult<Value> = {
16
16
  readonly ok: false;
17
17
  readonly error: string;
18
18
  };
19
+ /** Converts an array of string literals into a `{ Key: 'Key' }` record type. */
20
+ type ArrayToEnum<T extends readonly string[]> = {
21
+ readonly [K in T[number]]: K;
22
+ };
19
23
  interface EnumInstance<T extends Record<string, string | number>> {
20
24
  /** The original definition object. */
21
25
  readonly definition: T;
@@ -52,25 +56,25 @@ interface EnumInstance<T extends Record<string, string | number>> {
52
56
  keyOf(value: T[keyof T]): keyof T | undefined;
53
57
  }
54
58
  /**
55
- * Define a type-safe enum from a plain object.
56
- * No `as const` needed — literal types are inferred automatically.
59
+ * Define a type-safe enum from a plain object or an array of strings.
57
60
  *
58
61
  * @example
62
+ * // Object form — explicit key-value mapping:
59
63
  * const Status = defineEnum({
60
64
  * Active: 'active',
61
65
  * Inactive: 'inactive',
62
66
  * Pending: 'pending',
63
67
  * });
64
- *
65
68
  * type Status = InferValue<typeof Status>;
66
69
  * // 'active' | 'inactive' | 'pending'
67
70
  *
68
- * Status.values() // ['active', 'inactive', 'pending']
69
- * Status.keys() // ['Active', 'Inactive', 'Pending']
70
- * Status.is('active') // true (type guard)
71
- * Status.parse('xxx') // { ok: false, error: '...' }
72
- * Status.keyOf('active') // 'Active'
71
+ * @example
72
+ * // Array form — shorthand when key and value are the same:
73
+ * const Direction = defineEnum(['Up', 'Down', 'Left', 'Right']);
74
+ * type Direction = InferValue<typeof Direction>;
75
+ * // 'Up' | 'Down' | 'Left' | 'Right'
73
76
  */
77
+ declare function defineEnum<const T extends readonly string[]>(members: T): EnumInstance<ArrayToEnum<T>>;
74
78
  declare function defineEnum<const T extends Record<string, string | number>>(definition: T): EnumInstance<T>;
75
79
 
76
80
  export { type EnumInstance, type InferKey, type InferValue, type ParseResult, defineEnum };
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
- function l(n){let s=Object.values(n),u=Object.keys(n),a=Object.entries(n),t=new Set(Object.values(n)),o=new Map;for(let[e,r]of Object.entries(n))o.set(r,e);return {definition:n,values(){return s},keys(){return u},entries(){return a},is(e){return t.has(e)},parse(e){if(t.has(e))return {ok:true,value:e};let r=s.map(String).join(", ");return {ok:false,error:`Invalid enum value: "${String(e)}". Expected one of: ${r}`}},keyOf(e){return o.get(e)}}}export{l as defineEnum};//# sourceMappingURL=index.js.map
1
+ function u(e){let r=Object.values(e),s=Object.keys(e),y=Object.entries(e),o=new Set(Object.values(e)),a=new Map;for(let[n,t]of Object.entries(e))a.set(t,n);return {definition:e,values(){return r},keys(){return s},entries(){return y},is(n){return o.has(n)},parse(n){if(o.has(n))return {ok:true,value:n};let t=r.map(String).join(", ");return {ok:false,error:`Invalid enum value: "${String(n)}". Expected one of: ${t}`}},keyOf(n){return a.get(n)}}}function T(e){if(Array.isArray(e)){let r={};for(let s of e)r[s]=s;return u(r)}return u(e)}export{T as defineEnum};//# sourceMappingURL=index.js.map
2
2
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts"],"names":["defineEnum","definition","allValues","allKeys","allEntries","valueSet","reverseLookup","key","value","validValues"],"mappings":"AAiFO,SAASA,CAAAA,CACdC,CAAAA,CACiB,CACjB,IAAMC,CAAAA,CAAY,MAAA,CAAO,MAAA,CAAOD,CAAU,CAAA,CACpCE,CAAAA,CAAU,MAAA,CAAO,IAAA,CAAKF,CAAU,EAChCG,CAAAA,CAAa,MAAA,CAAO,OAAA,CAAQH,CAAU,CAAA,CAEtCI,CAAAA,CAAW,IAAI,GAAA,CAAqB,MAAA,CAAO,MAAA,CAAOJ,CAAU,CAAC,CAAA,CAC7DK,CAAAA,CAAgB,IAAI,GAAA,CAE1B,IAAA,GAAW,CAACC,CAAAA,CAAKC,CAAK,CAAA,GAAK,MAAA,CAAO,OAAA,CAAQP,CAAU,CAAA,CAClDK,CAAAA,CAAc,GAAA,CAAIE,CAAAA,CAAOD,CAAG,CAAA,CAG9B,OAAO,CACL,UAAA,CAAAN,CAAAA,CAEA,MAAA,EAAS,CACP,OAAOC,CACT,CAAA,CAEA,IAAA,EAAO,CACL,OAAOC,CACT,CAAA,CAEA,OAAA,EAAU,CACR,OAAOC,CACT,CAAA,CAEA,EAAA,CAAGI,CAAAA,CAAqC,CACtC,OAAOH,CAAAA,CAAS,GAAA,CAAIG,CAAwB,CAC9C,CAAA,CAEA,KAAA,CAAMA,CAAAA,CAAyC,CAC7C,GAAIH,CAAAA,CAAS,GAAA,CAAIG,CAAwB,CAAA,CACvC,OAAO,CAAE,EAAA,CAAI,IAAA,CAAM,KAAA,CAAOA,CAAoB,CAAA,CAGhD,IAAMC,CAAAA,CAAcP,CAAAA,CAAU,IAAI,MAAM,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA,CACnD,OAAO,CACL,EAAA,CAAI,KAAA,CACJ,KAAA,CAAO,CAAA,qBAAA,EAAwB,MAAA,CAAOM,CAAK,CAAC,CAAA,oBAAA,EAAuBC,CAAW,CAAA,CAChF,CACF,CAAA,CAEA,KAAA,CAAMD,CAAAA,CAAmB,CACvB,OAAOF,CAAAA,CAAc,GAAA,CAAIE,CAAwB,CACnD,CACF,CACF","file":"index.js","sourcesContent":["/**\n * The type of values in an enum definition.\n * Extracted as a union of all values.\n */\nexport type InferValue<T extends EnumInstance<Record<string, string | number>>> =\n ReturnType<T[\"values\"]>[number];\n\n/**\n * The type of keys in an enum definition.\n * Extracted as a union of all keys.\n */\nexport type InferKey<T extends EnumInstance<Record<string, string | number>>> =\n ReturnType<T[\"keys\"]>[number];\n\n/** The result of `.parse()` — compatible with `ts-safe-result`'s Result shape. */\nexport type ParseResult<Value> =\n | { readonly ok: true; readonly value: Value }\n | { readonly ok: false; readonly error: string };\n\nexport interface EnumInstance<T extends Record<string, string | number>> {\n /** The original definition object. */\n readonly definition: T;\n\n /** Get all values as a readonly array. */\n values(): readonly T[keyof T][];\n\n /** Get all keys as a readonly array. */\n keys(): readonly (keyof T)[];\n\n /** Get all entries as readonly [key, value] pairs. */\n entries(): readonly (readonly [keyof T, T[keyof T]])[];\n\n /**\n * Type guard — check if an unknown value is a valid enum value.\n *\n * @example\n * if (Status.is(input)) {\n * // input is narrowed to 'active' | 'inactive' | 'pending'\n * }\n */\n is(value: unknown): value is T[keyof T];\n\n /**\n * Parse an unknown value into a typed enum value.\n * Returns `{ ok: true, value }` or `{ ok: false, error }`.\n *\n * The shape is compatible with `ts-safe-result` — you can wrap it\n * with `ok()`/`err()` for full chaining support.\n */\n parse(value: unknown): ParseResult<T[keyof T]>;\n\n /**\n * Get the key corresponding to a given value.\n * Returns undefined if the value is not in the enum.\n *\n * @example\n * Status.keyOf('active') // 'Active'\n */\n keyOf(value: T[keyof T]): keyof T | undefined;\n}\n\n/**\n * Define a type-safe enum from a plain object.\n * No `as const` needed — literal types are inferred automatically.\n *\n * @example\n * const Status = defineEnum({\n * Active: 'active',\n * Inactive: 'inactive',\n * Pending: 'pending',\n * });\n *\n * type Status = InferValue<typeof Status>;\n * // 'active' | 'inactive' | 'pending'\n *\n * Status.values() // ['active', 'inactive', 'pending']\n * Status.keys() // ['Active', 'Inactive', 'Pending']\n * Status.is('active') // true (type guard)\n * Status.parse('xxx') // { ok: false, error: '...' }\n * Status.keyOf('active') // 'Active'\n */\nexport function defineEnum<const T extends Record<string, string | number>>(\n definition: T,\n): EnumInstance<T> {\n const allValues = Object.values(definition) as unknown as T[keyof T][];\n const allKeys = Object.keys(definition) as unknown as (keyof T)[];\n const allEntries = Object.entries(definition) as unknown as (readonly [keyof T, T[keyof T]])[];\n\n const valueSet = new Set<string | number>(Object.values(definition));\n const reverseLookup = new Map<string | number, string>();\n\n for (const [key, value] of Object.entries(definition)) {\n reverseLookup.set(value, key);\n }\n\n return {\n definition,\n\n values() {\n return allValues;\n },\n\n keys() {\n return allKeys;\n },\n\n entries() {\n return allEntries;\n },\n\n is(value: unknown): value is T[keyof T] {\n return valueSet.has(value as string | number);\n },\n\n parse(value: unknown): ParseResult<T[keyof T]> {\n if (valueSet.has(value as string | number)) {\n return { ok: true, value: value as T[keyof T] };\n }\n\n const validValues = allValues.map(String).join(\", \");\n return {\n ok: false,\n error: `Invalid enum value: \"${String(value)}\". Expected one of: ${validValues}`,\n };\n },\n\n keyOf(value: T[keyof T]) {\n return reverseLookup.get(value as string | number) as keyof T | undefined;\n },\n };\n}\n"]}
1
+ {"version":3,"sources":["../src/index.ts"],"names":["createInstance","definition","allValues","allKeys","allEntries","valueSet","reverseLookup","key","value","validValues","defineEnum","input","member"],"mappings":"AAkEA,SAASA,CAAAA,CACPC,CAAAA,CACiB,CACjB,IAAMC,EAAY,MAAA,CAAO,MAAA,CAAOD,CAAU,CAAA,CACpCE,EAAU,MAAA,CAAO,IAAA,CAAKF,CAAU,CAAA,CAChCG,EAAa,MAAA,CAAO,OAAA,CAAQH,CAAU,CAAA,CAEtCI,CAAAA,CAAW,IAAI,GAAA,CAAqB,MAAA,CAAO,OAAOJ,CAAU,CAAC,CAAA,CAC7DK,CAAAA,CAAgB,IAAI,GAAA,CAE1B,IAAA,GAAW,CAACC,CAAAA,CAAKC,CAAK,CAAA,GAAK,MAAA,CAAO,OAAA,CAAQP,CAAU,CAAA,CAClDK,CAAAA,CAAc,GAAA,CAAIE,CAAAA,CAAOD,CAAG,CAAA,CAG9B,OAAO,CACL,UAAA,CAAAN,EAEA,MAAA,EAAS,CACP,OAAOC,CACT,EAEA,IAAA,EAAO,CACL,OAAOC,CACT,EAEA,OAAA,EAAU,CACR,OAAOC,CACT,EAEA,EAAA,CAAGI,CAAAA,CAAqC,CACtC,OAAOH,EAAS,GAAA,CAAIG,CAAwB,CAC9C,CAAA,CAEA,MAAMA,CAAAA,CAAyC,CAC7C,GAAIH,CAAAA,CAAS,GAAA,CAAIG,CAAwB,CAAA,CACvC,OAAO,CAAE,EAAA,CAAI,IAAA,CAAM,KAAA,CAAOA,CAAoB,EAGhD,IAAMC,CAAAA,CAAcP,CAAAA,CAAU,GAAA,CAAI,MAAM,CAAA,CAAE,IAAA,CAAK,IAAI,CAAA,CACnD,OAAO,CACL,EAAA,CAAI,KAAA,CACJ,MAAO,CAAA,qBAAA,EAAwB,MAAA,CAAOM,CAAK,CAAC,uBAAuBC,CAAW,CAAA,CAChF,CACF,CAAA,CAEA,MAAMD,CAAAA,CAAmB,CACvB,OAAOF,CAAAA,CAAc,GAAA,CAAIE,CAAwB,CACnD,CACF,CACF,CA2BO,SAASE,CAAAA,CAAWC,CAAAA,CAA4D,CACrF,GAAI,KAAA,CAAM,OAAA,CAAQA,CAAK,EAAG,CACxB,IAAMV,CAAAA,CAAqC,GAC3C,IAAA,IAAWW,CAAAA,IAAUD,CAAAA,CACnBV,CAAAA,CAAWW,CAAM,CAAA,CAAIA,CAAAA,CAEvB,OAAOZ,CAAAA,CAAeC,CAAU,CAClC,CAEA,OAAOD,CAAAA,CAAeW,CAAwC,CAChE","file":"index.js","sourcesContent":["/**\n * The type of values in an enum definition.\n * Extracted as a union of all values.\n */\nexport type InferValue<T extends EnumInstance<Record<string, string | number>>> =\n ReturnType<T[\"values\"]>[number];\n\n/**\n * The type of keys in an enum definition.\n * Extracted as a union of all keys.\n */\nexport type InferKey<T extends EnumInstance<Record<string, string | number>>> =\n ReturnType<T[\"keys\"]>[number];\n\n/** The result of `.parse()` — compatible with `ts-safe-result`'s Result shape. */\nexport type ParseResult<Value> =\n | { readonly ok: true; readonly value: Value }\n | { readonly ok: false; readonly error: string };\n\n/** Converts an array of string literals into a `{ Key: 'Key' }` record type. */\ntype ArrayToEnum<T extends readonly string[]> = {\n readonly [K in T[number]]: K;\n};\n\nexport interface EnumInstance<T extends Record<string, string | number>> {\n /** The original definition object. */\n readonly definition: T;\n\n /** Get all values as a readonly array. */\n values(): readonly T[keyof T][];\n\n /** Get all keys as a readonly array. */\n keys(): readonly (keyof T)[];\n\n /** Get all entries as readonly [key, value] pairs. */\n entries(): readonly (readonly [keyof T, T[keyof T]])[];\n\n /**\n * Type guard — check if an unknown value is a valid enum value.\n *\n * @example\n * if (Status.is(input)) {\n * // input is narrowed to 'active' | 'inactive' | 'pending'\n * }\n */\n is(value: unknown): value is T[keyof T];\n\n /**\n * Parse an unknown value into a typed enum value.\n * Returns `{ ok: true, value }` or `{ ok: false, error }`.\n *\n * The shape is compatible with `ts-safe-result` — you can wrap it\n * with `ok()`/`err()` for full chaining support.\n */\n parse(value: unknown): ParseResult<T[keyof T]>;\n\n /**\n * Get the key corresponding to a given value.\n * Returns undefined if the value is not in the enum.\n *\n * @example\n * Status.keyOf('active') // 'Active'\n */\n keyOf(value: T[keyof T]): keyof T | undefined;\n}\n\nfunction createInstance<T extends Record<string, string | number>>(\n definition: T,\n): EnumInstance<T> {\n const allValues = Object.values(definition) as unknown as T[keyof T][];\n const allKeys = Object.keys(definition) as unknown as (keyof T)[];\n const allEntries = Object.entries(definition) as unknown as (readonly [keyof T, T[keyof T]])[];\n\n const valueSet = new Set<string | number>(Object.values(definition));\n const reverseLookup = new Map<string | number, string>();\n\n for (const [key, value] of Object.entries(definition)) {\n reverseLookup.set(value, key);\n }\n\n return {\n definition,\n\n values() {\n return allValues;\n },\n\n keys() {\n return allKeys;\n },\n\n entries() {\n return allEntries;\n },\n\n is(value: unknown): value is T[keyof T] {\n return valueSet.has(value as string | number);\n },\n\n parse(value: unknown): ParseResult<T[keyof T]> {\n if (valueSet.has(value as string | number)) {\n return { ok: true, value: value as T[keyof T] };\n }\n\n const validValues = allValues.map(String).join(\", \");\n return {\n ok: false,\n error: `Invalid enum value: \"${String(value)}\". Expected one of: ${validValues}`,\n };\n },\n\n keyOf(value: T[keyof T]) {\n return reverseLookup.get(value as string | number) as keyof T | undefined;\n },\n };\n}\n\n/**\n * Define a type-safe enum from a plain object or an array of strings.\n *\n * @example\n * // Object form — explicit key-value mapping:\n * const Status = defineEnum({\n * Active: 'active',\n * Inactive: 'inactive',\n * Pending: 'pending',\n * });\n * type Status = InferValue<typeof Status>;\n * // 'active' | 'inactive' | 'pending'\n *\n * @example\n * // Array form — shorthand when key and value are the same:\n * const Direction = defineEnum(['Up', 'Down', 'Left', 'Right']);\n * type Direction = InferValue<typeof Direction>;\n * // 'Up' | 'Down' | 'Left' | 'Right'\n */\nexport function defineEnum<const T extends readonly string[]>(\n members: T,\n): EnumInstance<ArrayToEnum<T>>;\nexport function defineEnum<const T extends Record<string, string | number>>(\n definition: T,\n): EnumInstance<T>;\nexport function defineEnum(input: readonly string[] | Record<string, string | number>) {\n if (Array.isArray(input)) {\n const definition: Record<string, string> = {};\n for (const member of input as readonly string[]) {\n definition[member] = member;\n }\n return createInstance(definition) as EnumInstance<any>;\n }\n\n return createInstance(input as Record<string, string | number>) as EnumInstance<any>;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-safe-enum",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "A tiny, type-safe alternative to TypeScript enums. Built on 'as const' objects with full inference, no runtime surprises.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",