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
Template literal types allow you to manipulate string types using the same syntax as JavaScript template literals. Combined with infer, they enable powerful string parsing and transformation at the type level.
type Greeting = `Hello, ${string}`;
const valid: Greeting = "Hello, World"; // OK
const invalid: Greeting = "Hi, World"; // Error: doesn't match patternTemplate literals distribute over unions:
type Size = "small" | "medium" | "large";
type Color = "red" | "blue" | "green";
type SizedColor = `${Size}-${Color}`;
// "small-red" | "small-blue" | "small-green" |
// "medium-red" | "medium-blue" | "medium-green" |
// "large-red" | "large-blue" | "large-green"inferExtract parts of string types:
// Remove "maps:" prefix
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"type RemovePostSuffix<T> = T extends `${infer Prefix}:post` ? Prefix : T;
type Test = RemovePostSuffix<"attribute:post">; // "attribute"type Split<S extends string, D extends string> =
S extends `${infer Head}${D}${infer Tail}`
? [Head, ...Split<Tail, D>]
: S extends ""
? []
: [S];
type Parts = Split<"a-b-c", "-">; // ["a", "b", "c"]TypeScript provides utility types for case conversion:
type Upper = Uppercase<"hello">; // "HELLO"
type Lower = Lowercase<"HELLO">; // "hello"
type Cap = Capitalize<"hello">; // "Hello"
type Uncap = Uncapitalize<"Hello">; // "hello"type CamelCase<S extends string> =
S extends `${infer P1}-${infer P2}${infer P3}`
? `${Lowercase<P1>}${Uppercase<P2>}${CamelCase<P3>}`
: Lowercase<S>;
type Test = CamelCase<"background-color">; // "backgroundColor"
type Test2 = CamelCase<"border-top-width">; // "borderTopWidth"type EventName<T extends string> = `on${Capitalize<T>}`;
type MouseEvents = "click" | "mousedown" | "mouseup";
type MouseHandlers = EventName<MouseEvents>;
// "onClick" | "onMousedown" | "onMouseup"type Getter<T extends string> = `get${Capitalize<T>}`;
type Setter<T extends string> = `set${Capitalize<T>}`;
type PropName = "name" | "age";
type Getters = Getter<PropName>; // "getName" | "getAge"
type Setters = Setter<PropName>; // "setName" | "setAge"type AddPrefix<T, P extends string> = {
[K in keyof T as K extends string ? `${P}${K}` : K]: T[K];
};
interface User {
name: string;
age: number;
}
type PrefixedUser = AddPrefix<User, "user_">;
// { user_name: string; user_age: number }type AddSuffix<T, S extends string> = {
[K in keyof T as K extends string ? `${K}${S}` : never]: T[K];
};
interface Data {
a: number;
b: number;
}
type NewData = AddSuffix<Data, "_new">;
// { a_new: number; b_new: number }type SnakeToCamel<S extends string> =
S extends `${infer P1}_${infer P2}${infer P3}`
? `${Lowercase<P1>}${Uppercase<P2>}${SnakeToCamel<P3>}`
: S;
type CamelizeKeys<T> = {
[K in keyof T as K extends string ? SnakeToCamel<K> : K]: T[K];
};
interface ApiResponse {
user_id: string;
first_name: string;
last_name: string;
}
type CamelResponse = CamelizeKeys<ApiResponse>;
// { userId: string; firstName: string; lastName: string }type ExtractRouteParams<T extends string> =
T extends `${string}:${infer Param}/${infer Rest}`
? Param | ExtractRouteParams<`/${Rest}`>
: T extends `${string}:${infer Param}`
? Param
: never;
type Params = ExtractRouteParams<"/users/:userId/posts/:postId">;
// "userId" | "postId"
// Create typed params object
type RouteParams<T extends string> = {
[K in ExtractRouteParams<T>]: string;
};
type UserPostParams = RouteParams<"/users/:userId/posts/:postId">;
// { userId: string; postId: string }type ValidEmail = `${string}@${string}.${string}`;
function validateEmail<T extends string>(
email: T extends ValidEmail ? T : never
): T {
return email;
}
validateEmail("user@example.com"); // OK
validateEmail("invalid"); // Errortype Protocol = "http" | "https";
type ValidUrl = `${Protocol}://${string}`;
function fetchUrl(url: ValidUrl): Promise<Response> {
return fetch(url);
}
fetchUrl("https://api.example.com"); // OK
fetchUrl("ftp://files.example.com"); // Errortype ParseQueryString<T extends string> =
T extends `${infer Key}=${infer Value}&${infer Rest}`
? { [K in Key]: Value } & ParseQueryString<Rest>
: T extends `${infer Key}=${infer Value}`
? { [K in Key]: Value }
: {};
type QueryParams = ParseQueryString<"name=John&age=30&city=NYC">;
// { name: "John" } & { age: "30" } & { city: "NYC" }type ParsePath<T extends string> =
T extends `${infer Key}.${infer Rest}`
? [Key, ...ParsePath<Rest>]
: [T];
type Path = ParsePath<"user.address.city">; // ["user", "address", "city"]TypeScript has recursion limits. Very deep template literal operations may fail:
// May hit recursion limit with very long strings
type DeepSplit<S extends string> = S extends `${infer H}${infer T}`
? [H, ...DeepSplit<T>]
: [];Template literals match greedily:
// This captures everything before the LAST .json
type GetPath<T> = T extends `${infer Path}.json` ? Path : never;
type Test = GetPath<"folder/file.backup.json">;
// "folder/file.backup" (includes the extra .backup)Template literals only work with string keys:
type AddPrefix<T, P extends string> = {
// Need to check K extends string to filter out symbols
[K in keyof T as K extends string ? `${P}${K}` : never]: T[K];
};Uppercase, Lowercase, etc. over custom implementations