Skip to content

REUSABLE TYPES · LESSON 13 OF 24

Literal objects, satisfies and enums

Keep precise values while checking a configuration contract.

  • as const
  • satisfies
  • enum
  • literal unions

Your runnable checkpoint

Extract into a new practice folder. Each download includes complete source, package.json, tsconfig.json and an offline lesson. Use Node.js 22 or 24. Each checkpoint is independent.

Install with npm install. Run npm run check, then npm run build. Terminal lessons use npm start; browser lessons use npm run serve and http://127.0.0.1:4173.

1. Understand the concepts

as const gives literal expressions narrow literal types and readonly properties. It is not Object.freeze and does not deeply freeze referenced objects at runtime.

satisfies checks that an expression is compatible with a target type without replacing its resulting type with that target. Combining it with as const is useful for configuration that should retain exact literals.

Enums create named constants and usually emit a runtime object. String enums give readable runtime values. A literal union or const object often works well when you want JavaScript-shaped code and easy data interchange.

Do not confuse a checked configuration with checked input. A parsed server response still needs runtime validation, even if you cast it to the type of this configuration.

2. Build and run

These are complete files. Preserve the paths shown below, including the src directory. The package and compiler configuration are included at the end.

src/main.ts

src/main.ts
type Route = { path: string; method: "GET" | "POST" };
const routes = {
  list: { path: "/tasks", method: "GET" },
  create: { path: "/tasks", method: "POST" }
} as const satisfies Record<string, Route>;
enum Priority { Low = "low", High = "high" }
const selected: "GET" = routes.list.method;
console.log(selected);
console.log(Priority.High);

package.json

package.json
{
  "name": "edupoly-typescript-literal-objects-satisfies-enums",
  "private": true,
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "check": "tsc --noEmit",
    "build": "tsc",
    "start": "node dist/main.js"
  },
  "devDependencies": {
    "typescript": "5.9.3"
  }
}

tsconfig.json

tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "lib": [
      "ES2022",
      "DOM"
    ],
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noEmitOnError": true,
    "verbatimModuleSyntax": true,
    "rootDir": "src",
    "outDir": "dist",
    "sourceMap": true,
    "types": [],
    "moduleDetection": "force"
  },
  "include": [
    "src/**/*"
  ]
}

3. Trace the code and check the result

  1. satisfies rejects a misspelled method such as GTE.
  2. as const retains GET instead of widening it to string.
  3. Priority.High is a runtime value; a union alias would be erased.

Expected result

GET
high

4. Practice independently

Add a delete route and make the method contract accept it.

Show the explanation

Add "DELETE" to Route["method"], then add a delete entry. Without updating the contract, satisfies should report an error.

When something goes wrong

If tsc is missing, run npm install in the folder containing package.json. If the output looks old, rebuild before running. Read the first compiler diagnostic and check the indicated field or argument. For module errors, preserve the .js extensions in imports. Browser examples must run through the included local server, not directly as TypeScript files.

Read the related official TypeScript documentation ↗