type-enum-dict
Type / Enum / Dict Skill
This skill covers three tightly-related artifacts created together whenever a domain concept needs a constrained string type at compile time and/or at runtime.
The three artifacts
Type (src/type/T{Name}.ts)
Pure TypeScript — no runtime value. Three variants:
String union — for finite known values:
type TPublishState = "draft" | "published";
export default TPublishState;
Template literal — for structured string shapes validated statically by the type system:
type TSimpleDate = `${number}-${number}-${number}`;
export default TSimpleDate;
Namespace-augmented — when sub-groups of the union are useful independently:
type TContentType =
| TContentType.Raster
| TContentType.Vector;
namespace TContentType {
export type Raster = "image/png" | "image/jpeg";
export type Vector = "image/svg+xml";
}
export default TContentType;
Template literal types generally don't need an Enum or Desc — there are no finite members to enumerate.
Enum (src/enum/{Name}Enum.ts)
Runtime identity record: every key equals its value. Relies on the global utility type Enum<T> = { [K in T]: K } (no import needed — it's a global declaration).
import TPublishState from "../type/TPublishState.js";
const PublishStateEnum: Enum<TPublishState> = {
draft: "draft",
published: "published",
};
export default PublishStateEnum;
Use Object.values(SomeEnum) to get an array of all members at runtime.
The type source can also come from a DTO interface member instead of a dedicated type file:
import IMyDto from "../dto/IMyDto.js";
const MyRoleEnum: Enum<IMyDto.Role> = { admin: "admin", viewer: "viewer" };
Dict (src/dict/{Name}Desc.ts)
Maps every union member to a string. Relies on the global utility type Desc<T> = { [K in T]: string }. Prefer using the enum constants as computed keys — this avoids magic strings and ensures the dict stays in sync with the enum.
Label dict — human-readable display values:
import TPublishState from "../type/TPublishState.js";
import PublishStateEnum from "../enum/PublishStateEnum.js";
const PublishStateDesc: Desc<TPublishState> = {
[PublishStateEnum.draft]: "Draft",
[PublishStateEnum.published]: "Published",
};
export default PublishStateDesc;
Value-mapping dict — when the mapped value is not a label (e.g. file extension, icon name, color):
// Name the file to signal the mapping direction: {Target}From{Source}.ts
const ExtensionFromContentType: Desc<TContentType> = {
"image/png": "png",
"image/svg+xml": "svg",
};
export default ExtensionFromContentType;
File naming conventions
| Artifact | File | Export name |
|---|---|---|
| Type | src/type/T{Name}.ts |
TName (default) |
| Enum | src/enum/{Name}Enum.ts |
{Name}Enum (default) |
| Label dict | src/dict/{Name}Desc.ts |
{Name}Desc (default) |
| Value-mapping dict | src/dict/{Target}From{Source}.ts |
descriptive (default) |
Barrel exports
Always update the barrel index after creating a file.
src/type/index.ts — type-only re-exports:
export type { default as TPublishState } from "./TPublishState.js";
src/enum/index.ts and src/dict/index.ts — value re-exports:
export { default as PublishStateEnum } from "./PublishStateEnum.js";
export { default as PublishStateDesc } from "./PublishStateDesc.js";
Path aliases
Use the package's configured path alias for cross-directory imports (e.g. ~/type/..., ~/enum/...). Check tsconfig.json for the alias prefix — don't hardcode deep relative paths.
Decision guide
| Situation | What to create |
|---|---|
| Finite string values (states, phases, categories) | Type + Enum + Desc |
| Structured string shape (date, time, code pattern) | Type only |
| Values with logical sub-groups (e.g. MIME types by category) | Type with namespace sub-types |
| Need to map each member to a non-label value | Additional value-mapping dict |
| Type already exists on a DTO interface | Enum only (skip type file) |
Workflow
- Identify the concept and its values (or pattern for template literals).
- Create the type file.
- Create the enum file (skip for template literals).
- Create the dict file(s) — clarify with the user what label or mapping is needed if not obvious from context.
- Add all new files to their respective
index.tsbarrel. - Run
typecheckto verify — theEnum<T>andDesc<T>constraints will catch any missing or extra members at compile time.
More from efesto-cloud/skills
persistence
>
18population
>
17value-object
|
17usecase
>
16entity
Create or modify domain entities using the @efesto-cloud/entity package. Use this skill whenever the user asks to add a new entity, update an existing entity, add properties or methods to an entity, or work on the entity/dto layer. Trigger when the user says things like "create a Foo entity", "add a field to Bar", "I need a new domain object", or "add entity X". Also trigger for DTO creation or modification.
16webapp-loader-action
>
11