TypeScript Features I Actually Use Every Week (Not the Flashy Ones)
3 min read
TypeScript listicles love the exotic: recursive conditional types, mapped-type gymnastics, type-level parsers. Impressive, and almost none of it appears in my actual work. This is the opposite list — the features that earn a place in production code every week on a healthcare platform, chosen by usage frequency rather than impressiveness. Three deep-dives and one honest recap.
Template literal types
String types that follow a pattern. The everyday use isn't clever string manipulation — it's making "stringly-typed" APIs honest:
type PillarId = 'frameworks' | 'javascript' | 'css-ui';
type TagClass = `tag--${PillarId}`;
const cls: TagClass = 'tag--javascript'; // ✓
const bad: TagClass = 'tag--jvascript'; // ✗ compile error
// Or: event names that must follow a convention
type EntityEvent = `${'user' | 'order'}:${'created' | 'updated' | 'deleted'}`;
// 'user:created' | 'user:updated' | ... — all six, generatedAnywhere your codebase has naming conventions — CSS classes, event names, route paths, permission strings — template literal types turn the convention from a code-review comment into a compiler rule. Typos in these strings used to be runtime bugs; now they're red squiggles.
The satisfies operator
The fix for a dilemma that annoyed me for years. Annotate a config object and you get checking but lose inference precision; skip the annotation and you keep precision but lose checking:
type Config = Record<string, { url: string; timeout?: number }>;
// Old dilemma, option A: checked, but every value widens to the Config shape
const services: Config = {
auth: { url: '/auth', timeout: 5000 },
};
// services.auth.timeout — fine, but TS forgot which keys exist
// satisfies: checked AND precisely inferred
const services2 = {
auth: { url: '/auth', timeout: 5000 },
billing: { url: '/billing' },
} satisfies Config;
services2.billing; // ✓ TS knows the exact keys
services2.auth.timeout.toFixed(0); // ✓ knows auth HAS timeout, billing doesn'tsatisfies validates the value against the type without widening it. Every config
object, theme definition, and route table I write now ends with a satisfies clause —
it's the "have your cake and check it too" operator.
Const assertions
as const tells TypeScript to infer the narrowest possible type — literals instead of
string, readonly tuples instead of arrays:
const STATUSES = ['draft', 'review', 'published'] as const;
// readonly ['draft', 'review', 'published'] — not string[]
type Status = (typeof STATUSES)[number];
// 'draft' | 'review' | 'published' — derived, not duplicatedThat last line is the weekly workhorse: one runtime array, one derived type, zero
drift. Add 'archived' to the array and the Status type, every exhaustive switch,
and every validator using it update or error accordingly. Without as const, the array
is string[] and the type must be maintained by hand alongside it — which is how the
type and the runtime list end up disagreeing quietly. It's the same
derive-don't-duplicate principle that makes
discriminated unions work.
Utility types (the recap)
I've written a full post on these, so just the
honest usage report: Pick/Omit weekly for shaping API payloads, Partial for every
PATCH endpoint, Record for every lookup table, ReturnType whenever a function
already knows a shape I'd otherwise duplicate. They're the standard library of the
type system — unglamorous, constant.
The quick reference
| Feature | What it's for | Weekly use case |
|---|---|---|
| Template literal types | Pattern-following strings | Event names, CSS classes, routes |
satisfies | Check without widening | Configs, themes, route tables |
as const | Narrowest inference, readonly | Derive union types from runtime arrays |
| Utility types | Reshape existing types | API payloads, PATCH bodies, lookups |
The connecting thread: every one of these is about making the code you already have the single source of truth — deriving types from values, validating without overwriting inference, turning conventions into contracts. That's the actual daily job of TypeScript. The flashy stuff is a hobby; this is the craft.