Step 3 — Refining types with tags
The Bookmark interface from the last step accepts any string for url and any string array for tags. That’s not realistic — we want url to actually be a URL, and we’d like tags to be a non-empty array of short strings.
typia adds these constraints through type tags that you intersect into the base type. They are real TypeScript types, so the compiler enforces correctness (no typos in tag names), and the runtime validator picks them up automatically.
Tightening Bookmark
Replace your bookmark.ts with this version:
import { tags } from "typia";
export interface Bookmark {
/**
* Stable identifier. UUID v4.
*/
id: string & tags.Format<"uuid">;
/**
* The URL we're saving.
*/
url: string & tags.Format<"url"> & tags.MaxLength<2048>;
/**
* Human-readable title for the link.
*/
title: string & tags.MinLength<1> & tags.MaxLength<200>;
/**
* Optional one-paragraph description.
*/
description?: string & tags.MaxLength<5000>;
/**
* Tags for filtering / grouping.
* At least one tag, no more than 16, each ≤ 32 characters.
*/
tags: Array<string & tags.MaxLength<32>>
& tags.MinItems<1>
& tags.MaxItems<16>;
/**
* When the bookmark was created.
*/
createdAt: string & tags.Format<"date-time">;
}What changed:
idmust be a UUIDurlmust be a URL of at most 2 048 characterstitlemust be between 1 and 200 charactersdescriptionis still optional but capped at 5 000 characterstagsis a 1–16 element array; each element is at most 32 characterscreatedAtmust be an ISO-8601 date-time
Everything else stays the same. The validators we wrote in step 2 immediately pick up the new constraints — no changes to their call sites.
See the new errors
import typia from "typia";
import { Bookmark } from "./bookmark";
const candidate: unknown = {
id: "not-a-uuid",
url: "lol",
title: "",
tags: [],
createdAt: "yesterday",
};
const result = typia.validate<Bookmark>(candidate);
if (!result.success) {
for (const err of result.errors) {
console.log(`${err.path}: expected ${err.expected}, got`, err.value);
}
}Run it:
npx ttsx src/03-tags.ts$input.id: expected string & Format<"uuid">, got 'not-a-uuid'
$input.url: expected string & Format<"url">, got 'lol'
$input.title: expected string & MinLength<1>, got ''
$input.tags: expected Array<…> & MinItems<1>, got []
$input.createdAt: expected string & Format<"date-time">, got 'yesterday'The expected strings echo back the same intersection syntax you wrote in bookmark.ts. If you ever wonder “what was supposed to be there?”, the error literally tells you.
The tag catalogue (the parts we’ll use)
| Tag | Base type | What it does |
|---|---|---|
tags.Format<"uuid" | "email" | "url" | "date" | "date-time" | …> | string | Built-in format check |
tags.MinLength<N> / tags.MaxLength<N> | string | Length bounds |
tags.Pattern<"..."> | string | Regex |
tags.Minimum<N> / tags.Maximum<N> | number, bigint | Range |
tags.Type<"int32" | "uint32" | "float" | ...> | number, bigint | Sub-type |
tags.MinItems<N> / tags.MaxItems<N> | Array<T> | Length bounds |
tags.UniqueItems | Array<T> | Pairwise distinct |
The full list is on the Special Tags page. Tags compose with & and | like any other TypeScript type — you can build expressions like (number & tags.Type<"int32">) | (bigint & tags.Type<"uint64">).
Bonus — making test data that satisfies the type
Now that the type is precise, typia.random can produce realistic instances:
import typia from "typia";
import { Bookmark } from "./bookmark";
const fake = typia.random<Bookmark>();
console.log(fake);{
id: '8d62f9c0-...',
url: 'http://example.com/...',
title: 'p0WkQNbe...',
tags: [ 'L8b', 'kHrJ' ],
createdAt: '2049-...'
}You get a value that satisfies every constraint on the type. Useful for test fixtures and for seeding development databases.
What you’ve done
- Replaced loose primitive types with tagged variants that carry constraints
- Watched the validator’s error messages tighten automatically
- Generated realistic fake bookmarks straight from the type
Next we’ll save and load these bookmarks to disk — and use typia to make sure the file we read back actually contains what we expect.