Capitalize<StringType>
Uppercases the first character of a string literal type, leaving the rest untouched.
type Capitalize<S extends string> = intrinsic;- Added in TypeScript 4.1
Parameters
| Parameter | Type | Description |
|---|---|---|
$S | string | A string literal type or a union of them. Only the first character is affected. |
Return value
A string literal type with the first character uppercasedThe literal type with only its first character uppercased. Distributes over unions; the empty string literal is returned unchanged.
Errors
tsc errors if the argument does not satisfy the `string` constraint.
Changelog
- PHP 4.1Introduced with template literal types. Briefly expressible in TypeScript during the beta, then reimplemented as a compiler intrinsic using `str.charAt(0).toUpperCase() + str.slice(1)`.
Capitalize touches exactly one character: the first. Everything after it is copied verbatim. That narrowness is the point — it is the piece that makes naming conventions expressible as types.
The canonical use is generating handler names. Given an event union "click" | "focus", the mapped type { [K in Event as `on${Capitalize<K>}`]: Handler } produces onClick and onFocus — the exact prop names a component API expects — derived from one source list. The same shape builds get/set accessor names from property names, handle* methods, and use* hook names. Add or rename an event and every derived key follows automatically, which is what makes this worth doing rather than typing both lists.
What trips people up
- It is not a snake-case-to-PascalCase converter.
Capitalize<"user_name">is"User_name", not"UserName"; converting separators needs recursive template literal types. - A leading character with no uppercase form — a digit, an underscore — is left alone, silently producing a key you may not have expected.
- Like the other three, it works on types only. The runtime string still has to be built by hand, usually with an assertion back to the derived type.
Example
When to use it
- Generate `on`-prefixed event handler prop names from an event-name union.
- Derive `getX`/`setX` accessor names from a union of property names.
- Build `handle*` method names for a class from the actions it supports.
Frequently asked questions
How do I build onClick-style key names from an event union?
Use a mapped type with key remapping and a template literal: `{ [K in Events as `on${Capitalize<K>}`]: Handler }`. The `as` clause rewrites each key, so `"click"` becomes `onClick`.
Why does Capitalize not convert snake_case to PascalCase?
Because it only uppercases the first character and copies the rest verbatim, so `"user_name"` becomes `"User_name"`. Removing separators requires a recursive template literal type that splits on the delimiter and capitalises each segment.
Is there a runtime equivalent of Capitalize?
The compiler uses `str.charAt(0).toUpperCase() + str.slice(1)` internally, and that is the expression to write at runtime. You will usually need `as Capitalize<T>` because the JavaScript result is typed as a plain `string`.
More examples
Accessor names generated from a data shape
`Getters<User>` resolves to `{ getName: () => string; getAge: () => number }`, so `user.getAge()` is typed `number` without any declaration of those methods. Output: `ada 36` then the sorted key list `[ 'age', 'getAge', 'getName', 'name' ]`.
type User = { name: string; age: number };
type Getters<T> = {
[K in keyof T & string as `get${Capitalize<K>}`]: () => T[K];
};
function withGetters<T extends object>(obj: T): T & Getters<T> {
const out: any = { ...obj };
for (const key of Object.keys(obj).sort()) {
out[`get${key.charAt(0).toUpperCase()}${key.slice(1)}`] = () => (obj as any)[key];
}
return out;
}
const user = withGetters<User>({ name: "ada", age: 36 });
console.log(user.getName(), user.getAge());
console.log(Object.keys(user).sort());Component props derived from an event list
Each handler's `event.type` is narrowed to its own literal — `onClick` receives `{ type: "click" }` and nothing else — because the key remapping keeps `E` bound per member. The run prints the two handler logs, then `label onBlur onClick`.
type DomEvent = "click" | "input" | "blur";
type Props = {
label: string;
} & {
[E in DomEvent as `on${Capitalize<E>}`]?: (event: { type: E }) => void;
};
const props: Props = {
label: "Save",
onClick: (e) => console.log("clicked", e.type),
onBlur: (e) => console.log("blurred", e.type),
};
props.onClick?.({ type: "click" });
props.onBlur?.({ type: "blur" });
console.log(Object.keys(props).sort().join(" "));
Discussion