Introduction
TypeScript provides four built-in string manipulation types that transform string literal types at the type level: Uppercase, Lowercase, Capitalize, and Uncapitalize. These are called "intrinsic" types because they are implemented directly in the compiler rather than as user-defined conditional types. Combined with template literals, they form a complete toolkit for type-level string processing.
Key Concepts
Uppercase<S>: Converts every character to uppercase:"hello"becomes"HELLO".Lowercase<S>: Converts every character to lowercase:"HELLO"becomes"hello".Capitalize<S>: Uppercases only the first character:"hello"becomes"Hello".Uncapitalize<S>: Lowercases only the first character:"Hello"becomes"hello".
Real World Context
These types are essential for generating type-safe API interfaces from schema definitions. CSS-in-JS libraries use them to type camelCase property names from kebab-case CSS properties. Event systems use Capitalize to generate handler names. Code generators use Uncapitalize to derive variable names from class names.
Deep Dive
The Four Intrinsic Types
Each type operates on string literal types:
typescripttype A = Uppercase<"hello">; // "HELLO" type B = Lowercase<"HELLO">; // "hello" type C = Capitalize<"hello">; // "Hello" type D = Uncapitalize<"Hello">; // "hello"
They also distribute over unions:
typescripttype Methods = "get" | "post" | "put" | "delete"; type UpperMethods = Uppercase<Methods>; // "GET" | "POST" | "PUT" | "DELETE"
Combining with Template Literals
The most common use is within template literal types:
typescripttype Events = "click" | "focus" | "blur"; // Generate handler names type OnHandler = `on${Capitalize<Events>}`; // "onClick" | "onFocus" | "onBlur" // Generate constant names type EventConst = `EVENT_${Uppercase<Events>}`; // "EVENT_CLICK" | "EVENT_FOCUS" | "EVENT_BLUR" // Generate CSS custom property names type CSSVar = `--${Lowercase<Events>}-color`; // "--click-color" | "--focus-color" | "--blur-color"
These patterns generate exhaustive string literal unions that prevent typos.
Pattern Matching and Extraction
Combine intrinsic types with infer to parse and transform strings:
typescript// Convert camelCase to snake_case (simplified for one word boundary) type CamelToSnake<S extends string> = S extends `${infer Head}${infer Tail}` ? Tail extends Capitalize<Tail> ? `${Lowercase<Head>}_${CamelToSnake<Uncapitalize<Tail>>}` : `${Lowercase<Head>}${CamelToSnake<Tail>}` : S;
While complex recursive string manipulation is possible, it is often better kept simple. The main practical use cases are:
typescript// Extract prefix and transform type RemovePrefix<S extends string, P extends string> = S extends `${P}${infer Rest}` ? Uncapitalize<Rest> : S; type A = RemovePrefix<"onClick", "on">; // "click" type B = RemovePrefix<"onMouseDown", "on">; // "mouseDown"
Building a Type-Safe Event System
Here is a complete example combining all four intrinsic types:
typescripttype DOMEvent = "click" | "mouseDown" | "keyPress" | "scroll"; // Handler names: onClick, onMouseDown, etc. type HandlerName<E extends string> = `on${Capitalize<E>}`; // Constant names: CLICK, MOUSE_DOWN, etc. (simplified) type ConstantName<E extends string> = Uppercase<E>; // CSS class names: event-click, event-mouse-down, etc. (simplified) type ClassName<E extends string> = `event-${Lowercase<E>}`; type ClickHandler = HandlerName<"click">; // "onClick" type ClickConst = ConstantName<"click">; // "CLICK" type ClickClass = ClassName<"click">; // "event-click"
Each intrinsic type handles a different naming convention from the same source.
Common Pitfalls
- Applying intrinsic types to
string(wide type) —Uppercase<string>returnsstring, not a useful narrowed type. Intrinsic types are most useful with string literal types or unions of literals. - Confusing
CapitalizewithUppercase—Capitalizeonly changes the first character;Uppercasechanges all characters.Capitalize<"hello">is"Hello", not"HELLO".
Best Practices
- Use
Capitalizewithonprefix for event handler patterns — The`on${Capitalize<EventName>}`pattern is the standard way to generate handler names in TypeScript. - Use
Uppercasefor enum-like constant naming — When deriving constant names from a union of lowercase values,Uppercaseproduces conventional SCREAMING_CASE identifiers.
Summary
Uppercase,Lowercase,Capitalize, andUncapitalizeare compiler-intrinsic string manipulation types.- They distribute over unions automatically.
- Combine them with template literals for type-safe string generation.
Capitalize+ template prefix is the standard event handler naming pattern.
Code Examples
// Building a type-safe API client from method definitions
type CRUDAction = "create" | "read" | "update" | "delete";
type Resource = "user" | "post";
// Generate method names: createUser, readPost, etc.
type APIMethod = `${CRUDAction}${Capitalize<Resource>}`;
// "createUser" | "createPost" | "readUser" | "readPost" |
// "updateUser" | "updatePost" | "deleteUser" | "deletePost"
// Generate a typed API client
type APIClient = {
[M in APIMethod]: () => Promise<unknown>;
};
// Implementation can be checked:
const client: APIClient = {
createUser: async () => {},
createPost: async () => {},
readUser: async () => {},
readPost: async () => {},
updateUser: async () => {},
updatePost: async () => {},
deleteUser: async () => {},
deletePost: async () => {},
// Missing or misspelled methods would be compile errors
};