snippettypescriptModeratepending
TypeScript conditional types for flexible APIs
Viewed 0 times
conditional-typeinferExtractExcludegenericreturn-type
Problem
Function return types need to change based on input types or options, but overloads are verbose and hard to maintain.
Solution
Use conditional types for dynamic return types:
// Return type depends on input
type StringOrNumber<T> = T extends string ? string : number;
function process<T extends string | number>(input: T): StringOrNumber<T> {
if (typeof input === 'string') return input.toUpperCase() as any;
return (input * 2) as any;
}
process('hello'); // type: string
process(42); // type: number
// Conditional based on options
interface FetchOptions<T extends boolean = false> {
url: string;
raw?: T;
}
type FetchResult<T extends boolean> = T extends true ? Response : any;
async function fetchData<T extends boolean = false>(
options: FetchOptions<T>
): Promise<FetchResult<T>> {
const res = await fetch(options.url);
if (options.raw) return res as any;
return res.json();
}
const data = await fetchData({ url: '/api' }); // type: any
const raw = await fetchData({ url: '/api', raw: true }); // type: Response
// Extract and Exclude utility types
type EventName = 'click' | 'scroll' | 'keydown' | 'keyup';
type KeyEvents = Extract<EventName,
type NonKeyEvents = Exclude<EventName,
// infer keyword for extracting types
type ReturnOf<T> = T extends (...args: any[]) => infer R ? R : never;
type PromiseValue<T> = T extends Promise<infer V> ? V : T;
// Return type depends on input
type StringOrNumber<T> = T extends string ? string : number;
function process<T extends string | number>(input: T): StringOrNumber<T> {
if (typeof input === 'string') return input.toUpperCase() as any;
return (input * 2) as any;
}
process('hello'); // type: string
process(42); // type: number
// Conditional based on options
interface FetchOptions<T extends boolean = false> {
url: string;
raw?: T;
}
type FetchResult<T extends boolean> = T extends true ? Response : any;
async function fetchData<T extends boolean = false>(
options: FetchOptions<T>
): Promise<FetchResult<T>> {
const res = await fetch(options.url);
if (options.raw) return res as any;
return res.json();
}
const data = await fetchData({ url: '/api' }); // type: any
const raw = await fetchData({ url: '/api', raw: true }); // type: Response
// Extract and Exclude utility types
type EventName = 'click' | 'scroll' | 'keydown' | 'keyup';
type KeyEvents = Extract<EventName,
key${string}>; // 'keydown' | 'keyup'type NonKeyEvents = Exclude<EventName,
key${string}>; // 'click' | 'scroll'// infer keyword for extracting types
type ReturnOf<T> = T extends (...args: any[]) => infer R ? R : never;
type PromiseValue<T> = T extends Promise<infer V> ? V : T;
Why
Conditional types make APIs self-documenting: the return type adapts to how you call the function, giving precise types without manual assertions.
Revisions (0)
No revisions yet.