REUSABLE TYPES · LESSON 12 OF 24
Mapped, conditional and template literal types
Build small type transformations and understand their limits.
- mapped types
- conditional types
- infer
- template literals
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
A mapped type iterates over a union of keys. It can preserve, add or remove modifiers while transforming properties. Flags<T> maps every property of T to boolean.
A conditional type chooses a result based on assignability. infer introduces a type variable inside the matching branch. ElementOf extracts an array element type and otherwise leaves the input unchanged.
A conditional on a naked type parameter distributes over union members. ElementOf<string[] | number> therefore becomes string | number. Wrap both sides in tuples when you intentionally need a non-distributive check.
Template literal types build string patterns from type-level pieces. They are helpful for event names and constrained identifiers. They do not generate event handlers or validate external strings. Keep transformations readable; simpler explicit unions are often easier to maintain.
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
type Flags<T> = { [K in keyof T]: boolean };
type ElementOf<T> = T extends readonly (infer Item)[] ? Item : T;
type EventName = `${"task" | "user"}:${"created" | "deleted"}`;
const flags: Flags<{ email: string; name: string }> = { email: true, name: false };
const item: ElementOf<readonly number[]> = 42;
const event: EventName = "task:created";
console.log(flags.email);
console.log(item);
console.log(event);
package.json
{
"name": "edupoly-typescript-mapped-conditional-template-types",
"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
{
"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
- K visits each property key of T.
- infer Item captures the element type when T is an array.
- EventName permits four combinations and rejects unrelated strings.
Expected result
true
42
task:created4. Practice independently
Create a nullable version of each property in an object.
Show the explanation
Use type NullableFields<T> = { [K in keyof T]: T[K] | null }. This allows null values but keeps the original required or optional modifiers.
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.
