typescript-utility-types

TypeScript Utility Types

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "typescript-utility-types" with this command: npx skills add thebushidocollective/han/thebushidocollective-han-typescript-utility-types

TypeScript Utility Types

Master TypeScript's powerful type system including built-in utility types, mapped types, conditional types, and advanced type manipulation techniques for creating flexible, type-safe code.

Built-in Utility Types

Partial and Required

interface User { id: string; name: string; email: string; age: number; }

// Partial makes all properties optional type PartialUser = Partial<User>; // { id?: string; name?: string; email?: string; age?: number; }

function updateUser(id: string, updates: Partial<User>): User { const existingUser = getUser(id); return { ...existingUser, ...updates }; }

updateUser('123', { name: 'John' }); // Valid updateUser('123', { age: 30 }); // Valid

// Required makes all properties required interface OptionalConfig { host?: string; port?: number; timeout?: number; }

type RequiredConfig = Required<OptionalConfig>; // { host: string; port: number; timeout: number; }

function validateConfig(config: Required<OptionalConfig>): boolean { return config.host.length > 0 && config.port > 0; }

Pick and Omit

interface Article { id: string; title: string; content: string; author: string; createdAt: Date; updatedAt: Date; views: number; }

// Pick selects specific properties type ArticlePreview = Pick<Article, 'id' | 'title' | 'author'>; // { id: string; title: string; author: string; }

function displayPreview(article: ArticlePreview): void { console.log(${article.title} by ${article.author}); }

// Omit removes specific properties type ArticleWithoutDates = Omit<Article, 'createdAt' | 'updatedAt'>; // { id: string; title: string; content: string; author: string; views: number; }

// Combining Pick and Omit type ArticleMetadata = Pick<Article, 'id' | 'author' | 'createdAt'>; type ArticleData = Omit<Article, 'id' | 'createdAt' | 'updatedAt'>;

Readonly and Record

// Readonly makes all properties readonly type ReadonlyUser = Readonly<User>;

const user: ReadonlyUser = { id: '1', name: 'John', email: 'john@example.com', age: 30, };

// user.name = 'Jane'; // Error: Cannot assign to 'name' because it is a read-only property

// Deep readonly for nested objects type DeepReadonly<T> = { readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P]; };

// Record creates an object type with specific keys and value type type UserRole = 'admin' | 'editor' | 'viewer';

type RolePermissions = Record<UserRole, string[]>; // { admin: string[]; editor: string[]; viewer: string[]; }

const permissions: RolePermissions = { admin: ['read', 'write', 'delete'], editor: ['read', 'write'], viewer: ['read'], };

// Record with complex types type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE'; type RouteHandler = (req: Request) => Response;

type RouteHandlers = Record<HttpMethod, RouteHandler>;

Extract and Exclude

type Status = 'pending' | 'approved' | 'rejected' | 'cancelled';

// Extract types that are assignable to a condition type CompletedStatus = Extract<Status, 'approved' | 'rejected'>; // 'approved' | 'rejected'

// Exclude types that are assignable to a condition type ActiveStatus = Exclude<Status, 'approved' | 'rejected' | 'cancelled'>; // 'pending'

// Practical example type Shape = | { kind: 'circle'; radius: number } | { kind: 'square'; side: number } | { kind: 'rectangle'; width: number; height: number };

type CircularShape = Extract<Shape, { kind: 'circle' }>; // { kind: 'circle'; radius: number }

type NonCircularShape = Exclude<Shape, { kind: 'circle' }>; // { kind: 'square'; side: number } | { kind: 'rectangle'; width: number; height: number }

ReturnType and Parameters

function createUser(name: string, age: number): User { return { id: generateId(), name, age, email: ${name.toLowerCase()}@example.com, }; }

// ReturnType extracts the return type of a function type UserFromFunction = ReturnType<typeof createUser>; // User

// Parameters extracts parameter types as a tuple type CreateUserParams = Parameters<typeof createUser>; // [name: string, age: number]

// Using with generic functions function processData<T>(data: T[]): { count: number; items: T[] } { return { count: data.length, items: data }; }

type ProcessResult = ReturnType<typeof processData<User>>; // { count: number; items: User[] }

Mapped Types

Basic Mapped Types

// Create a type where all properties are boolean type Flags<T> = { [P in keyof T]: boolean; };

type UserFlags = Flags<User>; // { id: boolean; name: boolean; email: boolean; age: boolean; }

// Create a type where all properties are nullable type Nullable<T> = { [P in keyof T]: T[P] | null; };

type NullableUser = Nullable<User>; // { id: string | null; name: string | null; email: string | null; age: number | null; }

// Create a type where all properties are functions type Getters<T> = { [P in keyof T as get${Capitalize&#x3C;string &#x26; P>}]: () => T[P]; };

type UserGetters = Getters<User>; // { getId: () => string; getName: () => string; getEmail: () => string; getAge: () => number; }

Mapped Type Modifiers

// Remove readonly modifier type Mutable<T> = { -readonly [P in keyof T]: T[P]; };

interface ReadonlyPerson { readonly name: string; readonly age: number; }

type MutablePerson = Mutable<ReadonlyPerson>; // { name: string; age: number; }

// Remove optional modifier type Concrete<T> = { [P in keyof T]-?: T[P]; };

interface OptionalUser { name?: string; age?: number; }

type ConcreteUser = Concrete<OptionalUser>; // { name: string; age: number; }

// Add optional modifier type Optional<T> = { [P in keyof T]+?: T[P]; };

Advanced Mapped Types

// Transform property types type Promisify<T> = { [P in keyof T]: Promise<T[P]>; };

type AsyncUser = Promisify<User>; // { id: Promise<string>; name: Promise<string>; email: Promise<string>; age: Promise<number>; }

// Wrap values in objects type Boxed<T> = { [P in keyof T]: { value: T[P] }; };

type BoxedUser = Boxed<User>; // { id: { value: string }; name: { value: string }; ... }

// Create proxy type type Proxy<T> = { get(): T; set(value: T): void; };

type ProxiedProperties<T> = { [P in keyof T]: Proxy<T[P]>; };

Conditional Types

Basic Conditional Types

// T extends U ? X : Y type IsString<T> = T extends string ? true : false;

type Test1 = IsString<string>; // true type Test2 = IsString<number>; // false

// Nested conditionals type TypeName<T> = T extends string ? 'string' : T extends number ? 'number' : T extends boolean ? 'boolean' : T extends undefined ? 'undefined' : T extends Function ? 'function' : 'object';

type T0 = TypeName<string>; // 'string' type T1 = TypeName<number>; // 'number' type T2 = TypeName<() => void>; // 'function'

Distributive Conditional Types

// Conditional types distribute over union types type ToArray<T> = T extends any ? T[] : never;

type StrOrNumArray = ToArray<string | number>; // string[] | number[] (not (string | number)[])

// Non-distributive version type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;

type StrOrNumArrayNonDist = ToArrayNonDist<string | number>; // (string | number)[]

// Filter out null and undefined type NonNullable<T> = T extends null | undefined ? never : T;

type MaybeString = string | null | undefined; type DefinitelyString = NonNullable<MaybeString>; // string

Inferring Types with infer

// Infer return type type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function example(): { x: number } { return { x: 42 }; }

type ExampleReturn = GetReturnType<typeof example>; // { x: number }

// Infer array element type type Flatten<T> = T extends Array<infer U> ? U : T;

type Str = Flatten<string[]>; // string type Num = Flatten<number>; // number

// Infer Promise type type Awaited<T> = T extends Promise<infer U> ? U : T;

type PromiseString = Awaited<Promise<string>>; // string type RegularString = Awaited<string>; // string

// Multiple infer usage type GetFirstArg<T> = T extends (first: infer F, ...args: any[]) => any ? F : never;

function multi(a: string, b: number, c: boolean): void {}

type FirstArgType = GetFirstArg<typeof multi>; // string

Template Literal Types

Basic Template Literals

type World = 'world'; type Greeting = hello ${World}; // 'hello world'

// With unions type Color = 'red' | 'blue' | 'green'; type Quantity = 'one' | 'two';

type ColoredQuantity = ${Quantity} ${Color}; // 'one red' | 'one blue' | 'one green' | 'two red' | 'two blue' | 'two green'

// Event names type EventName = 'click' | 'focus' | 'blur'; type EventHandler = on${Capitalize&#x3C;EventName>}; // 'onClick' | 'onFocus' | 'onBlur'

String Manipulation Types

// Built-in string manipulation types type UppercaseGreeting = Uppercase<'hello'>; // 'HELLO' type LowercaseGreeting = Lowercase<'HELLO'>; // 'hello' type CapitalizedGreeting = Capitalize<'hello'>; // 'Hello' type UncapitalizedGreeting = Uncapitalize<'Hello'>; // 'hello'

// Combining with template literals type GetterName<T extends string> = get${Capitalize&#x3C;T>}; type SetterName<T extends string> = set${Capitalize&#x3C;T>};

type UserNameGetter = GetterName<'name'>; // 'getName' type UserNameSetter = SetterName<'name'>; // 'setName'

// Generate accessor methods type Accessors<T> = { [K in keyof T as GetterName<string & K>]: () => T[K]; } & { [K in keyof T as SetterName<string & K>]: (value: T[K]) => void; };

type UserAccessors = Accessors<User>; // { getName: () => string; setName: (value: string) => void; ... }

Pattern Matching with Template Literals

// Extract parts from string patterns type ExtractRouteParams<T extends string> = T extends ${infer Start}/:${infer Param}/${infer Rest} ? { [K in Param | keyof ExtractRouteParams</${Rest}>]: string } : T extends ${infer Start}/:${infer Param} ? { [K in Param]: string } : {};

type Route1 = ExtractRouteParams<'/users/:userId/posts/:postId'>; // { userId: string; postId: string; }

type Route2 = ExtractRouteParams<'/posts/:id'>; // { id: string; }

// Parse CSS properties type CSSProperty = | 'color' | 'background-color' | 'font-size' | 'margin-top';

type CamelCase<S extends string> = S extends ${infer P1}-${infer P2}${infer P3} ? ${P1}${Uppercase&#x3C;P2>}${CamelCase&#x3C;P3>} : S;

type CSSPropertyCamel = CamelCase<CSSProperty>; // 'color' | 'backgroundColor' | 'fontSize' | 'marginTop'

Key Remapping

Remapping Keys in Mapped Types

// Filter out specific keys type OmitByType<T, U> = { [P in keyof T as T[P] extends U ? never : P]: T[P]; };

interface Mixed { name: string; age: number; isActive: boolean; count: number; }

type OnlyStrings = OmitByType<Mixed, number | boolean>; // { name: string; }

// Rename keys with a prefix type Prefix<T, P extends string> = { [K in keyof T as ${P}${string &#x26; K}]: T[K]; };

type PrefixedUser = Prefix<User, 'user_'>; // { user_id: string; user_name: string; user_email: string; user_age: number; }

// Convert to getter methods type Getters<T> = { [K in keyof T as get${Capitalize&#x3C;string &#x26; K>}]: () => T[K]; };

type UserGetters = Getters<User>;

Conditional Key Remapping

// Only include keys that match a condition type PickByType<T, U> = { [P in keyof T as T[P] extends U ? P : never]: T[P]; };

type NumberProperties = PickByType<Mixed, number>; // { age: number; count: number; }

// Rename keys based on type type RenameByType<T> = { [K in keyof T as T[K] extends string ? str_${string &#x26; K} : T[K] extends number ? num_${string &#x26; K} : K]: T[K]; };

type RenamedMixed = RenameByType<Mixed>; // { str_name: string; num_age: number; isActive: boolean; num_count: number; }

Advanced Type Manipulation

Recursive Types

// JSON type type JSONValue = | string | number | boolean | null | JSONValue[] | { [key: string]: JSONValue };

const json: JSONValue = { name: 'John', age: 30, hobbies: ['reading', 'coding'], address: { city: 'New York', coordinates: [40.7128, -74.006], }, };

// Recursive path type type Path<T> = T extends object ? { [K in keyof T]: K extends string ? T[K] extends object ? K | ${K}.${Path&#x3C;T[K]>} : K : never; }[keyof T] : never;

type UserPath = Path<User>; // 'id' | 'name' | 'email' | 'age' | ...

// Deep partial type DeepPartial<T> = { [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P]; };

Union and Intersection Utilities

// Union to intersection type UnionToIntersection<U> = ( U extends any ? (x: U) => void : never ) extends (x: infer I) => void ? I : never;

type Union = { a: string } | { b: number }; type Intersection = UnionToIntersection<Union>; // { a: string } & { b: number }

// Get required keys type RequiredKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? never : K; }[keyof T];

interface PartialRequired { required: string; optional?: number; }

type Required = RequiredKeys<PartialRequired>; // 'required'

// Get optional keys type OptionalKeys<T> = { [K in keyof T]-?: {} extends Pick<T, K> ? K : never; }[keyof T];

type Optional = OptionalKeys<PartialRequired>; // 'optional'

Function Type Utilities

// Make function async type Asyncify<T extends (...args: any[]) => any> = ( ...args: Parameters<T> ) => Promise<ReturnType<T>>;

function syncFunction(x: number): string { return x.toString(); }

type AsyncFunction = Asyncify<typeof syncFunction>; // (x: number) => Promise<string>

// Curry function type type Curry<T> = T extends ( arg: infer A, ...args: infer R ) => infer Return ? (arg: A) => R extends [] ? Return : Curry<(...args: R) => Return> : never;

type CurriedFunction = Curry<(a: string, b: number, c: boolean) => void>; // (arg: string) => (arg: number) => (arg: boolean) => void

Builder Pattern Types

// Type-safe builder pattern type Builder<T, R = {}> = { [K in keyof T]: ( value: T[K] ) => Builder<Omit<T, K>, R & Pick<T, K>>; } & (R extends T ? { build(): T } : {});

interface Config { host: string; port: number; ssl: boolean; }

function createBuilder<T>(): Builder<T> { const values: Partial<T> = {};

const builder = new Proxy( {}, { get(_, prop) { if (prop === 'build') { return () => values as T; } return (value: any) => { values[prop as keyof T] = value; return builder; }; }, } ) as Builder<T>;

return builder; }

// Usage with full type safety const config = createBuilder<Config>() .host('localhost') .port(3000) .ssl(true) .build(); // Only available when all properties are set

Type Inference Helpers

Const Assertions

// Without const assertion const colors1 = ['red', 'blue', 'green']; type Colors1 = typeof colors1; // string[]

// With const assertion const colors2 = ['red', 'blue', 'green'] as const; type Colors2 = typeof colors2; // readonly ['red', 'blue', 'green'] type Color = Colors2[number]; // 'red' | 'blue' | 'green'

// Object with const assertion const config = { api: { url: 'https://api.example.com', timeout: 5000, }, } as const;

type ConfigUrl = typeof config.api.url; // 'https://api.example.com'

Type Guards with User-Defined Type Guards

function isString(value: unknown): value is string { return typeof value === 'string'; }

function isUser(value: unknown): value is User { return ( typeof value === 'object' && value !== null && 'id' in value && 'name' in value && 'email' in value ); }

// Generic type guard factory function hasProperty<K extends string>( key: K ): <T>(obj: T) => obj is T & Record<K, unknown> { return (obj): obj is T & Record<K, unknown> => { return typeof obj === 'object' && obj !== null && key in obj; }; }

const hasName = hasProperty('name');

if (hasName(someObject)) { console.log(someObject.name); // Type-safe access }

Best Practices

Prefer Built-in Utility Types: Use TypeScript's built-in utility types (Partial, Pick, Omit, etc.) before creating custom ones for better readability.

Use Const Assertions: Apply const assertions to arrays and objects when you need literal types instead of widened types.

Keep Types Simple: Avoid overly complex type transformations. If a type becomes hard to understand, consider refactoring or using multiple simpler types.

Document Complex Types: Add comments to explain non-obvious type transformations, especially for mapped and conditional types.

Leverage Type Inference: Let TypeScript infer types when possible rather than explicitly declaring them everywhere.

Use Template Literal Types for Strings: For string patterns and concatenation, template literal types provide type safety that plain strings cannot.

Prefer Type over Interface for Utilities: Use type aliases for utility types and mapped types, as they're more flexible than interfaces.

Test Your Types: Write test cases for complex types using type assertions to ensure they behave as expected.

Avoid Type Gymnastics: Don't create complex types just because you can. Focus on types that add value and clarity to your code.

Use Discriminated Unions: For variant types, use discriminated unions with a literal type field for better type narrowing.

Common Pitfalls

Excessive Type Complexity: Creating overly complex types makes code harder to understand and can slow down the TypeScript compiler.

Ignoring Type Distribution: Forgetting that conditional types distribute over unions can lead to unexpected type results.

Misusing ReturnType with Generics: Using ReturnType on generic functions without providing type arguments loses type information.

Circular Type References: Creating circular type dependencies can cause TypeScript errors or infinite type recursion.

Over-using any: Using any in utility types defeats their purpose and loses type safety benefits.

Not Understanding Mapped Type Modifiers: Misusing + and - modifiers or forgetting them can produce unexpected readonly/optional behavior.

Template Literal Performance: Complex template literal types with many unions can significantly slow down type checking.

Forgetting as const: Not using const assertions when you need literal types results in widened types that lose specificity.

Mismatched Conditional Types: Writing conditional type conditions that never match or always match makes types useless.

Utility Type Overkill: Creating utility types for simple operations that could be expressed directly makes code harder to read.

When to Use This Skill

Use TypeScript utility types when you need to:

  • Transform existing types without duplication

  • Create type-safe APIs and libraries

  • Build generic, reusable type utilities

  • Enforce type constraints at compile time

  • Generate types from runtime values

  • Create type-safe builders and fluent APIs

  • Model complex domain logic with types

  • Implement design patterns with type safety

  • Reduce type maintenance burden

  • Provide better IDE autocomplete and error messages

This skill is essential for library authors, framework developers, TypeScript experts, and anyone building type-safe, maintainable TypeScript applications.

Resources

Official Documentation

Learning Resources

Tools and Libraries

Community

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

typescript-type-system

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

typescript-async-patterns

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

c-systems-programming

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

cpp-templates-metaprogramming

No summary provided by upstream source.

Repository SourceNeeds Review