Designs complex generic types, refactors `any` types to strict alternatives, creates type guards and utility types, and resolves TypeScript compiler errors. Use when the user asks about TypeScript (TS) types, generics, type inference, type guards, removing `any` types, strict typing, type errors, `infer`, `extends`, conditional types, mapped types, template literal types, branded/opaque types, or utility types like `Partial`, `Record`, `ReturnType`, and `Awaited`.
97
97%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
infer KeywordThe infer keyword allows you to extract and capture type information within conditional types. It's like pattern matching for types - you define a pattern and capture parts of it.
type ExtractType<T> = T extends SomePattern<infer U> ? U : never;
// ^^^^^^^^
// Captures this part into Utype ArrayElement<T> = T extends (infer U)[] ? U : never;
type Test1 = ArrayElement<string[]>; // string
type Test2 = ArrayElement<number[]>; // number
type Test3 = ArrayElement<(string | number)[]>; // string | number
type Test4 = ArrayElement<string>; // never (not an array)type PromiseValue<T> = T extends Promise<infer U> ? U : never;
type Test1 = PromiseValue<Promise<string>>; // string
type Test2 = PromiseValue<Promise<number>>; // number
type Test3 = PromiseValue<string>; // nevertype GetData<T> = T extends { data: infer TData } ? TData : never;
type Test1 = GetData<{ data: string }>; // string
type Test2 = GetData<{ data: number[] }>; // number[]
type Test3 = GetData<{ other: string }>; // neverinfer works powerfully with template literal types:
// Remove "maps:" prefix from string
type RemoveMaps<T> = T extends `maps:${infer Rest}` ? Rest : T;
type Test1 = RemoveMaps<"maps:longitude">; // "longitude"
type Test2 = RemoveMaps<"maps:latitude">; // "latitude"
type Test3 = RemoveMaps<"other">; // "other" (no match, returns T)// Parse route parameters
type ParseRoute<T> = T extends `${infer Start}:${infer Param}/${infer Rest}`
? { start: Start; param: Param; rest: ParseRoute<Rest> }
: T extends `${infer Start}:${infer Param}`
? { start: Start; param: Param }
: T;
type Route = ParseRoute<"/users/:id/posts/:postId">;
// Nested structure with extracted params// Get everything before ":"
type Before<T> = T extends `${infer Prefix}:${string}` ? Prefix : T;
// Get everything after ":"
type After<T> = T extends `${string}:${infer Suffix}` ? Suffix : T;
type Test1 = Before<"prefix:suffix">; // "prefix"
type Test2 = After<"prefix:suffix">; // "suffix"type MyReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type Test = MyReturnType<() => string>; // stringtype MyParameters<T> = T extends (...args: infer P) => any ? P : never;
type Test = MyParameters<(a: string, b: number) => void>;
// [a: string, b: number]type FirstArg<T> = T extends (first: infer F, ...rest: any[]) => any
? F
: never;
type Test = FirstArg<(name: string, age: number) => void>; // stringtype ConstructorParams<T> = T extends new (...args: infer P) => any
? P
: never;
class User {
constructor(public name: string, public age: number) {}
}
type UserParams = ConstructorParams<typeof User>; // [string, number]infer in One ConditionYou can use multiple infer captures:
// Extract key-value from "key=value" string
type ParseKeyValue<T> = T extends `${infer Key}=${infer Value}`
? { key: Key; value: Value }
: never;
type Test = ParseKeyValue<"name=John">;
// { key: "name"; value: "John" }You can add constraints to inferred types:
// Only infer if it's a string
type ExtractString<T> = T extends { value: infer V extends string }
? V
: never;
type Test1 = ExtractString<{ value: "hello" }>; // "hello"
type Test2 = ExtractString<{ value: 123 }>; // never// Deeply unwrap nested promises
type DeepAwaited<T> = T extends Promise<infer U>
? DeepAwaited<U>
: T;
type Test = DeepAwaited<Promise<Promise<Promise<string>>>>; // stringtype EventHandler<T> = T extends (event: infer E) => void ? E : never;
interface Events {
click: (event: MouseEvent) => void;
keydown: (event: KeyboardEvent) => void;
}
type ClickEvent = EventHandler<Events["click"]>; // MouseEventtype ExtractParams<T extends string> =
T extends `${string}:${infer Param}/${infer Rest}`
? Param | ExtractParams<`/${Rest}`>
: T extends `${string}:${infer Param}`
? Param
: never;
type Params = ExtractParams<"/users/:userId/posts/:postId">;
// "userId" | "postId"// Remove "maps:" prefix from all object keys
type RemoveMaps<T> = T extends `maps:${infer Rest}` ? Rest : T;
type RemoveMapsPrefixFromObj<T> = {
[K in keyof T as RemoveMaps<K>]: T[K];
};
interface ApiData {
"maps:longitude": string;
"maps:latitude": string;
city: string;
}
type Cleaned = RemoveMapsPrefixFromObj<ApiData>;
// { longitude: string; latitude: string; city: string }type ExtractGeneric<T> = T extends Array<infer U>
? U
: T extends Map<infer K, infer V>
? { key: K; value: V }
: T extends Set<infer U>
? U
: never;
type Test1 = ExtractGeneric<Array<string>>; // string
type Test2 = ExtractGeneric<Map<string, number>>; // { key: string; value: number }
type Test3 = ExtractGeneric<Set<boolean>>; // boolean// Captures the FIRST matching position
type First<T> = T extends [infer F, ...any[]] ? F : never;
type Last<T> = T extends [...any[], infer L] ? L : never;
type TestFirst = First<[1, 2, 3]>; // 1
type TestLast = Last<[1, 2, 3]>; // 3// Greedy: captures as much as possible
type GetPath<T> = T extends `${infer Path}.json` ? Path : never;
type Test = GetPath<"folder/file.name.json">;
// "folder/file.name" (not "folder/file")type ExtractArray<T> = T extends (infer U)[] ? U : never;
// Distributes over union
type Test = ExtractArray<string[] | number[]>;
// string | number (not (string | number)[])inferfalse branchTData, TKey, TValue