install
source · Clone the upstream repo
git clone https://github.com/Intense-Visions/harness-engineering
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/claude-code/ts-mapped-types" ~/.claude/skills/intense-visions-harness-engineering-ts-mapped-types && rm -rf "$T"
manifest:
agents/skills/claude-code/ts-mapped-types/SKILL.mdsource content
TypeScript Mapped Types
Transform object types by iterating over their keys with mapped type syntax
When to Use
- Creating new types by transforming all properties of an existing type
- Making all properties optional, readonly, or nullable
- Remapping keys to create derived property names
- Building type-safe form state, API response wrappers, or configuration objects
Instructions
- Basic mapped type — iterate over keys with
:in keyof
type Readonly<T> = { readonly [K in keyof T]: T[K]; }; type Optional<T> = { [K in keyof T]?: T[K]; };
- Add or remove modifiers with
and+
:-
type Mutable<T> = { -readonly [K in keyof T]: T[K]; }; type Required<T> = { [K in keyof T]-?: T[K]; };
- Transform value types:
type Nullable<T> = { [K in keyof T]: T[K] | null; }; type Getters<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]; }; type User = { name: string; age: number }; type UserGetters = Getters<User>; // { getName: () => string; getAge: () => number }
- Key remapping with
:as
type PrefixedKeys<T, P extends string> = { [K in keyof T as `${P}${Capitalize<string & K>}`]: T[K]; }; type ApiUser = PrefixedKeys<User, 'user'>; // { userName: string; userAge: number }
- Filter keys with
andas
:never
type OnlyStrings<T> = { [K in keyof T as T[K] extends string ? K : never]: T[K]; }; type StringFields = OnlyStrings<{ name: string; age: number; email: string }>; // { name: string; email: string }
- Map over a union of string literals:
type EventHandlers<T extends string> = { [K in T as `on${Capitalize<K>}`]: (event: K) => void; }; type Handlers = EventHandlers<'click' | 'hover' | 'focus'>; // { onClick: (event: 'click') => void; onHover: ...; onFocus: ... }
- Combine with conditional types:
type DeepPartial<T> = { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]; };
- Practical patterns:
// Form state with touched/error tracking type FormState<T> = { values: T; touched: { [K in keyof T]: boolean }; errors: { [K in keyof T]?: string }; }; // Pick and transform type PickAndNullify<T, K extends keyof T> = { [P in K]: T[P] | null; };
Details
Mapped types iterate over a set of keys (from
keyof T or a string literal union) and produce a new object type with one property per key. They are the foundation of most built-in utility types (Partial, Required, Readonly, Pick, Record).
Homomorphic mapped types: When the key source is
keyof T, the mapped type preserves optional and readonly modifiers from the original type. This is why Partial<T> works correctly on types that already have optional properties.
Key remapping (TypeScript 4.1+): The
as clause in a mapped type lets you transform key names, filter keys (by mapping to never), or create entirely new key sets from the original keys.
Template literal types + mapped types: Combining
as \prefix${K}`` with mapped types creates patterns like getter/setter generation, event handler typing, and API route typing.
Recursive mapped types: TypeScript allows recursive mapped types for deep transformations (
DeepPartial, DeepReadonly). The compiler has a recursion depth limit — extremely deep object nesting can cause issues.
Trade-offs:
- Mapped types produce clear, readable type transformations but complex mapped types can be hard to debug
- Key remapping with
is powerful but the type error messages when it fails are often crypticas - Recursive mapped types can slow the compiler on deeply nested structures
Source
https://typescriptlang.org/docs/handbook/2/mapped-types.html
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.