git clone https://github.com/Intense-Visions/harness-engineering
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/codex/ts-utility-types" ~/.claude/skills/intense-visions-harness-engineering-ts-utility-types-187024 && rm -rf "$T"
agents/skills/codex/ts-utility-types/SKILL.mdTypeScript Utility Types
Apply built-in TypeScript utility types to transform and compose types without redundancy
When to Use
- You need a variant of an existing type with some properties optional, required, or removed
- You want to derive a type from a function's return value or parameter list without duplicating it
- You need to build a lookup map type from a union of keys
- You want to filter a union type to include or exclude specific members
- You are building form types, API response shapes, or partial update payloads
Instructions
- Use
for optional-update payloads where every field is optional.Partial<T> - Use
to make every optional field mandatory (opposite of Partial).Required<T> - Use
to create a type with only the listed keys from T.Pick<T, K> - Use
to create a type with all keys except the listed ones.Omit<T, K> - Use
to define a mapping type — prefer over index signatures when keys are known.Record<K, V> - Use
to remove union members assignable to U from T.Exclude<T, U> - Use
to keep only union members assignable to U.Extract<T, U> - Use
to derive the return type of a function without manually declaring it.ReturnType<typeof fn> - Use
to derive the parameter tuple of a function.Parameters<typeof fn> - Chain utility types when needed:
.Partial<Pick<User, 'name' | 'email'>>
interface User { id: string; name: string; email: string; role: 'admin' | 'viewer' | 'editor'; createdAt: Date; } // Partial update payload — all fields optional type UserUpdate = Partial<Omit<User, 'id' | 'createdAt'>>; // Public profile — only safe fields type PublicUser = Pick<User, 'id' | 'name' | 'role'>; // Role lookup map type RoleConfig = Record<User['role'], { canWrite: boolean; canDelete: boolean }>; // Exclude admin from public-facing role type type PublicRole = Exclude<User['role'], 'admin'>; // 'viewer' | 'editor' // Infer return type without coupling to implementation async function fetchUser(id: string): Promise<User> { /* ... */ return {} as User; } type FetchUserResult = Awaited<ReturnType<typeof fetchUser>>; // User // Infer parameters for wrapping/mocking type FetchUserParams = Parameters<typeof fetchUser>; // [id: string]
Details
All utility types are implemented as generic conditional or mapped types in TypeScript's standard library (
lib.es5.d.ts). Understanding their implementation helps you build custom variants when the built-ins don't cover your case.
Implementation anatomy
// Partial<T> implementation type Partial<T> = { [P in keyof T]?: T[P] }; // Pick<T, K> implementation type Pick<T, K extends keyof T> = { [P in K]: T[P] }; // Exclude<T, U> implementation type Exclude<T, U> = T extends U ? never : T; // ReturnType<T> implementation type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any;
vs OmitExclude
Omit<T, K> operates on object types and removes properties. Exclude<T, U> operates on union types and removes members. They are not interchangeable.
vs index signaturesRecord
Record<string, V> and { [key: string]: V } are equivalent for most purposes, but Record<K, V> with a union K creates a required entry for each union member — the index signature does not.
Composing utility types
Utility types compose via nesting. Deep transformations may require recursive types (TypeScript 4.1+) or a custom mapped type. For deeply partial types, the standard library's
Partial only goes one level deep.
NonNullable<T>
Removes
null and undefined from a type. Equivalent to Exclude<T, null | undefined>. Useful after null-safety guards.
and ConstructorParameters<T>InstanceType<T>
Less common but useful for class-based code:
ConstructorParameters extracts the constructor's args, InstanceType extracts what new T() produces.
When to write custom utilities instead
When none of the built-ins express your transformation, write a mapped or conditional type. The built-ins are a starting point, not a ceiling.
Source
https://typescriptlang.org/docs/handbook/utility-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.