keyof, typeof & Template Literals
Lift values into types and build string-based type systems with keyof, typeof, and template literals.
You've learned how to transform and filter types with mapped and conditional types. But where do types come from in the first place? Often, they come from values — runtime constants, configuration objects, or string literals. TypeScript gives you three powerful operators to bridge the gap between values and types:
keyof T— produces a union of an object type's keys.typeof value— lifts a runtime value into the type world.- Template literal types — builds new string literal types from existing ones, with full string manipulation.
Together, these tools let you derive types from your code's structure, ensuring that types and values stay in sync automatically. You'll build event systems, configuration validators, and API contracts where the types are generated from your data, not manually duplicated.
keyof T: The Keys of a Type
The keyof operator takes an object type and produces a union of its keys (as string or number literal types).
keyof User resolves to "id" | "name" | "email". This is useful when you want to accept any valid key of an object, but reject invalid ones:
Notice that T[K] is an indexed access type: the type of property K in T. Since K extends keyof T, TypeScript knows K is a valid key, and can look up its type.
typeof value: From Values to Types
The typeof operator (in a type context) takes a runtime value and produces its type. This lets you derive types from constants, configuration objects, or function declarations.
typeof config produces { apiUrl: string; timeout: number; retries: number }. If you add or remove properties from config, the type automatically reflects the change.
You can also use typeof with functions:
Combining keyof and typeof
When you have a value (not a type), you can combine typeof and keyof to get its keys:
Here, typeof routes lifts the object into a type, and keyof extracts "home" | "about" | "contact". This pattern is common for configuration objects, enums, and lookup tables.
Template Literal Types
TypeScript 4.1 introduced template literal types, which let you construct new string literal types by interpolating existing types. The syntax mirrors JavaScript template literals:
When you interpolate a union, TypeScript distributes over it, producing all combinations:
Intrinsic String Manipulation Types
TypeScript provides four built-in utility types for manipulating string literals:
Uppercase<S>— convertsSto uppercase.Lowercase<S>— convertsSto lowercase.Capitalize<S>— capitalizes the first character.Uncapitalize<S>— lowercases the first character.
These are especially useful in template literals:
Practical Example: Type-Safe Event Handlers
Let's build a type-safe event system where handler names are derived from event names:
Notice how:
EventMapdefines the payload types for each event.HandlerName<E>transforms event names ("click") into handler names ("onClick").- The mapped type
Handlersiterates over event names and remaps them to handler names, assigning each a function type that accepts the correct payload.
This pattern ensures that if you add a new event to EventMap, the corresponding handler is automatically required in Handlers, with the correct payload type. No manual duplication.
Filtering Keys by Value Type
You can combine keyof, template literals, and conditional types to filter keys based on their value types. For example, extracting only the string properties:
Here's how it works:
- The mapped type produces
{ id: never; name: "name"; email: "email"; active: never }. - Indexing with
[keyof T]extracts the values:never | "name" | "email" | never. nevervanishes from the union, leaving"name" | "email".
Challenge: Build EventHandlerMap<T>
Given an event map like { click: {...}, focus: {...} }, build a type that maps each event name to a handler function name (prefixed with on and capitalized), with the correct payload type.
For example:
type Events = { click: { x: number }; focus: { id: string } };
type Handlers = EventHandlerMap<Events>;
// Result: { onClick: (payload: { x: number }) => void; onFocus: (payload: { id: string }) => void }
Hints:
- Use a mapped type with key remapping:
[E in keyof T as ...]. - Use a template literal to build the handler name:
on${Capitalize<E>}. - The value type should be a function
(payload: T[E]) => void.
Multiple Choice: keyof typeof
Given:
const colors = { red: "#f00", green: "#0f0", blue: "#00f" };
type ColorName = keyof typeof colors;
What is ColorName?
string
"red" | "green" | "blue"
"#f00" | "#0f0" | "#00f"
Multiple Choice: Template Literal Distribution
Given:
type Prefix = "get" | "set";
type Prop = "Name" | "Age";
type Combined = `${Prefix}${Prop}`;
How many string literal types does Combined contain?
2
4
8
Summary
You've learned how to:
- Use
keyof Tto extract a union of object keys. - Use
typeof valueto lift runtime values into the type world. - Combine
keyofandtypeofto derive types from configuration objects. - Build new string literal types with template literals.
- Manipulate strings with
Uppercase,Lowercase,Capitalize,Uncapitalize. - Create type-safe mappings between related string-based concepts (events → handlers).
These tools let you write types that are computed from your code's structure, rather than manually maintained in parallel. When you change a configuration object or add a new event, the types update automatically — the compiler becomes your co-maintainer.
Next: Branded Types — adding nominal typing to TypeScript's structural system to prevent mixing semantically distinct values.