Dataslope logoDataslope

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:

  1. keyof T — produces a union of an object type's keys.
  2. typeof value — lifts a runtime value into the type world.
  3. 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).

Code Block
TypeScript 5.7

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:

Code Block
TypeScript 5.7

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.

Code Block
TypeScript 5.7

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:

Code Block
TypeScript 5.7

Combining keyof and typeof

When you have a value (not a type), you can combine typeof and keyof to get its keys:

Code Block
TypeScript 5.7

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:

Code Block
TypeScript 5.7

When you interpolate a union, TypeScript distributes over it, producing all combinations:

Code Block
TypeScript 5.7

Intrinsic String Manipulation Types

TypeScript provides four built-in utility types for manipulating string literals:

  • Uppercase<S> — converts S to uppercase.
  • Lowercase<S> — converts S to lowercase.
  • Capitalize<S> — capitalizes the first character.
  • Uncapitalize<S> — lowercases the first character.
Code Block
TypeScript 5.7

These are especially useful in template literals:

Code Block
TypeScript 5.7

Practical Example: Type-Safe Event Handlers

Let's build a type-safe event system where handler names are derived from event names:

Code Block
TypeScript 5.7

Notice how:

  1. EventMap defines the payload types for each event.
  2. HandlerName<E> transforms event names ("click") into handler names ("onClick").
  3. The mapped type Handlers iterates 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:

Code Block
TypeScript 5.7

Here's how it works:

  1. The mapped type produces { id: never; name: "name"; email: "email"; active: never }.
  2. Indexing with [keyof T] extracts the values: never | "name" | "email" | never.
  3. never vanishes from the union, leaving "name" | "email".

Challenge: Build EventHandlerMap<T>

Challenge
TypeScript 5.7
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

QuestionSelect one

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

QuestionSelect one

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 T to extract a union of object keys.
  • Use typeof value to lift runtime values into the type world.
  • Combine keyof and typeof to 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.

On this page