TypeScript 37 ๐ท Utility Types โ ThisType, Uppercase, Lowercase, Capitalize
The standard library’s last group of utilities covers two very different concerns. ThisType<T> controls what this means inside object literals โ a niche but essential tool for libraries that want methods sharing state. Uppercase<T>, Lowercase<T>, Capitalize<T>, and Uncapitalize<T> are intrinsic string types โ the compiler computes them. They transform string types and power the template-literal patterns from Chapter 33. Together they complete the utility-types tour: ThisType for typing this, the four case helpers for string transformation.
Key point: ThisType<T> doesn’t add anything to a type โ it’s a marker that tells TypeScript what this should be inside the methods of an object literal. The case helpers are intrinsic โ the compiler implements them directly because string case conversion isn’t expressible with normal type operations. Both are specialized; both matter where they apply.
ThisType<T> โ controlling this in object literals
ThisType<T> is a marker that sets the type of this inside an object literal’s methods.
type User = {
name: string;
greet: () => string;
} & ThisType<{ name: string }>;
const user: User = {
name: 'Alice',
greet() {
return `Hello, ${this.name}`; // this: { name: string }
}
};
Without ThisType, this inside greet would be the object literal’s own type โ usable, but not typed as { name: string }. With ThisType, this is exactly that.
How ThisType works: It’s an empty interface โ no members, no runtime existence. The compiler reads it as a directive: “inside this type’s methods, this has type T.”
What ThisType does:
- Sets
thisinside methods of an object literal - Applies only in intersections or as part of an object type
- Doesn’t add members to the type
What ThisType doesn’t do:
- Doesn’t work outside object literals
- Doesn’t change runtime behavior
- Doesn’t apply to classes (they have their own
thistyping)
Why it’s rare: Most code uses classes, where this is typed automatically. ThisType is for the pattern where you build objects with methods and want this to point at a shared context โ the “mixin-like” pattern.
Why it exists: Before classes were common in TypeScript code, object literals with methods were the norm. ThisType gives them the same this typing classes have. It’s still used by libraries that build objects dynamically.
Why “ThisType”: It’s a type for
this. The name describes exactly what it controls. The compiler treats it specially โ it’s not a normal type, it’s a directive. That’s why it lives in the standard library as a special case rather than being user-defined.
The ThisType pattern in practice
The classic use case is a context object whose methods share state through this.
type CalculatorContext = {
result: number;
add: (n: number) => void;
subtract: (n: number) => void;
reset: () => void;
};
type Calculator = CalculatorContext & ThisType<CalculatorContext>;
function createCalculator(): Calculator {
return {
result: 0,
add(n) {
this.result += n; // this: CalculatorContext
},
subtract(n) {
this.result -= n;
},
reset() {
this.result = 0;
}
};
}
const calc = createCalculator();
calc.add(5);
calc.add(3);
calc.subtract(2);
calc.result; // 6
Every method’s this is typed as CalculatorContext โ the same shape, shared state.
Why this pattern matters: It lets you build stateful objects with methods, without classes. Useful for libraries that generate objects dynamically.
The intersection form: CalculatorContext & ThisType<CalculatorContext> โ the first gives the members, the second gives the this type.
Why both parts: The shape must be declared. The ThisType marker tells the compiler what this should be. Without the intersection, this isn’t typed as the context.
Vue and similar frameworks: The Options API pattern โ data, methods, computed โ is the classic use of ThisType. Each section’s methods get this typed as the combined options.
When ThisType is needed:
- Object factories that build methods sharing state
- Frameworks that compose objects from sections
- Anywhere you want
thistyped without classes
When it’s not: Almost everywhere else. Classes cover most cases; ThisType is the specialized tool.
Why not just use a class: Classes have a static shape โ one definition, many instances. Object factories can build different shapes at runtime.
ThisTypesupports the dynamic case while keepingthistyped.
The case helpers โ Uppercase, Lowercase, Capitalize, Uncapitalize
Four intrinsic string types transform string types.
type A = Uppercase<'hello'>; // 'HELLO'
type B = Lowercase<'HELLO'>; // 'hello'
type C = Capitalize<'hello'>; // 'Hello'
type D = Uncapitalize<'Hello'>; // 'hello'
Each transforms a string literal type.
What “intrinsic” means: The compiler implements these directly. You can’t write Uppercase yourself โ string case conversion isn’t expressible with type operations. TypeScript has them as built-in compiler features.
On unions:
type A = Uppercase<'a' | 'b' | 'c'>;
// 'A' | 'B' | 'C'
Each member is transformed independently.
On the general string type:
type A = Uppercase<string>; // string
When the input is string (not a literal), the output is string โ the transformation can’t be computed.
On template literals:
type Getter<K extends string> = `get${Capitalize<K>}`;
type A = Getter<'name'>; // 'getName'
type B = Getter<'name' | 'age'>; // 'getName' | 'getAge'
This is the primary use case โ generating names from string types.
On non-string types:
type A = Uppercase<number>; // number (unchanged)
type B = Uppercase<boolean>; // boolean (unchanged)
Non-strings pass through. The helpers only transform strings.
Case helpers distribute over unions because they’re applied to each member. That’s automatic โ you don’t have to do anything.
Why the helpers exist: Without them, generating names from string types wouldn’t be possible. They’re the primitives that make getter/setter generation, event-name derivation, and case-insensitive key matching expressible.
Why these are “intrinsic”: Normal types are composed from other types.
Array<T>is defined in terms ofT.Uppercase<T>can’t be โ you can’t write a rule that turns'a'into'A'using type operations. The compiler has to compute it. That’s what “intrinsic” means.
The case helpers in template literals
The four helpers shine inside template literal types.
Generating getters:
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
interface User {
name: string;
age: number;
}
type UserGetters = Getters<User>;
// {
// getName: () => string;
// getAge: () => number;
// }
Every key becomes get + the capitalized key.
Generating setters:
type Setters<T> = {
[K in keyof T as `set${Capitalize<string & K>}`]: (value: T[K]) => void;
};
Generating event handlers:
type Handlers<T> = {
[K in keyof T as `on${Capitalize<string & K>}`]: (payload: T[K]) => void;
};
Removing a prefix:
type RemoveGet<T> = {
[K in keyof T as K extends `get${infer R}` ? Uncapitalize<R> : K]: T[K];
};
interface Api {
getName: () => string;
version: string;
}
type Model = RemoveGet<Api>;
// {
// name: () => string;
// version: string;
// }
Uncapitalize turns 'Name' into 'name'.
Converting to camelCase:
type CamelCase<S extends string> =
S extends `${infer Head}_${infer Rest}`
? `${Head}${Capitalize<CamelCase<Rest>>}`
: S;
type A = CamelCase<'user_first_name'>;
// 'userFirstName'
Recursive template literal with Capitalize.
Converting to snake_case: The inverse, using Uncapitalize.
type SnakeCase<S extends string> =
S extends `${infer Head}${infer Rest}`
? Head extends Uppercase<Head>
? Head extends Lowercase<Head>
? `${Lowercase<Head>}${SnakeCase<Rest>}`
: `_${Lowercase<Head>}${SnakeCase<Rest>}`
: `${Head}${SnakeCase<Rest>}`
: S;
Complex, but shows the helpers compose recursively.
Why the helpers pair with templates: They transform the placeholder values in a template. Capitalize is the most common โ it turns 'name' into 'Name' for getName. Uncapitalize is the inverse โ used when stripping prefixes.
Why string & K: K can be string | number | symbol. The helpers require strings, so string & K narrows to the string part.
Why
string & Kand notK as string:string & Kis a type operation โ it produces the intersection ofstringandK. WhenKis a string literal, the intersection is the literal itself. WhenKisnumberorsymbol, the intersection isnever(and the helper ignores it). It’s the idiomatic way to narrow a key.
Practical uses of the case helpers
A few patterns show up repeatedly.
Type-safe CSS property names:
type CSSProperty = 'color' | 'font-size' | 'background';
type CamelCSS<K extends string> =
K extends `${infer A}-${infer B}`
? `${A}${Capitalize<B>}`
: K;
type CSSProp = CamelCSS<CSSProperty>;
// 'color' | 'fontSize' | 'background'
Event name generation:
type Events = 'click' | 'focus' | 'blur';
type Handlers = `on${Capitalize<Events>}`;
// 'onClick' | 'onFocus' | 'onBlur'
Case-insensitive key matching:
type IncludesIgnoreCase<T extends string, U extends string> =
Lowercase<T> extends Lowercase<U> ? true : false;
type A = IncludesIgnoreCase<'Hello', 'hello'>; // true
type B = IncludesIgnoreCase<'Hello', 'world'>; // false
Lowercase normalizes both sides for comparison.
Assertion of uppercase strings:
type IsUpper<S extends string> =
S extends Uppercase<S> ? true : false;
type A = IsUpper<'HELLO'>; // true
type B = IsUpper<'hello'>; // false
Type-level enum keys:
const STATUS = {
IDLE: 'idle',
READY: 'ready'
} as const;
type StatusKey = keyof typeof STATUS;
// 'IDLE' | 'READY'
type StatusKeyLower = Lowercase<StatusKey>;
// 'idle' | 'ready'
Why these patterns matter: They eliminate the need for hand-maintained lists. Convert once, and the type system knows every variant. Changing a source string updates every derived type.
Why they’re not always the answer: Sometimes a plain union is clearer. Uppercase<'a' | 'b'> is fine, but if the values never change, 'A' | 'B' is simpler. Use the helpers when the derivation is the point โ when the source could change.
Why the helpers are transformations, not validators: They produce a new type. They don’t check that a string is uppercase โ they compute the uppercase version. To check, compare with the helper’s result:
S extends Uppercase<S>.
A full example
A typed option-builder using ThisType and the case helpers.
// ============================================
// THIS TYPE PATTERN
// ============================================
interface RequestContext {
url: string;
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
headers: Record<string, string>;
body?: string;
}
type RequestBuilder = RequestContext & ThisType<RequestContext & {
setHeader: (key: string, value: string) => void;
}>;
function createRequest(url: string): RequestBuilder {
return {
url,
method: 'GET',
headers: {},
body: undefined,
setHeader(key: string, value: string) {
this.headers[key] = value;
}
};
}
const req = createRequest('/api/users');
req.method = 'POST';
req.headers['Content-Type'] = 'application/json';
req.body = '{"name":"Alice"}';
console.log(req);
// ============================================
// CASE HELPERS WITH TEMPLATES
// ============================================
// Generate getter names
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
interface Product {
id: number;
name: string;
price: number;
}
type ProductGetters = Getters<Product>;
// {
// getId: () => number;
// getName: () => string;
// getPrice: () => number;
// }
// ============================================
// EVENT HANDLERS
// ============================================
type EventNames = 'click' | 'focus' | 'blur' | 'keydown';
type Handlers = {
[K in EventNames as `on${Capitalize<K>}`]: (event: Event) => void;
};
const handlers: Partial<Handlers> = {
onClick: (e) => console.log('click', e),
onFocus: (e) => console.log('focus', e)
};
// ============================================
// REVERSE: EXTRACT KEY FROM GETTER
// ============================================
type GetterKey<S> =
S extends `get${infer K}` ? Uncapitalize<K> : never;
type A = GetterKey<'getName'>; // 'name'
type B = GetterKey<'getEmail'>; // 'email'
type C = GetterKey<'version'>; // never
// ============================================
// CASE-INSENSITIVE LOOKUP
// ============================================
type CaseInsensitiveKeys<T, K extends string> = {
[P in keyof T as Lowercase<string & P> extends Lowercase<K>
? P
: never
]: T[P];
};
interface Settings {
Theme: string;
Language: string;
Notifications: boolean;
}
type ThemeKey = CaseInsensitiveKeys<Settings, 'theme'>;
// { Theme: string }
// ============================================
// USAGE
// ============================================
const product: Product = { id: 1, name: 'Widget', price: 9.99 };
const getters: ProductGetters = {
getId: () => product.id,
getName: () => product.name,
getPrice: () => product.price
};
console.log(getters.getName());
console.log(getters.getId());
What this shows:
createRequestusesThisTypeto typethisinsidesetHeaderGetters<T>usesCapitalizein a mapped type to generateget*method namesHandlersusesCapitalizeto generateon*handlersGetterKeyusesUncapitalizeto reversegetNameโnameCaseInsensitiveKeysusesLowercaseto compare keys ignoring case
ThisType handles the this typing; the case helpers handle the string transformations.
Why this shape: It’s a realistic library-style module.
ThisTypebuilds objects with typedthis; the case helpers derive names. Both are used by real frameworks โ Vue’s Options API usesThisType; every library that generates method names uses the case helpers.
Complete Example Session
# ============================================
# PART 1: THISTYPE
# ============================================
cat > thistype.ts << 'EOF'
interface CalculatorContext {
result: number;
add: (n: number) => void;
subtract: (n: number) => void;
}
type Calculator = CalculatorContext & ThisType<CalculatorContext>;
function createCalculator(): Calculator {
return {
result: 0,
add(n) {
this.result += n;
},
subtract(n) {
this.result -= n;
}
};
}
const calc = createCalculator();
calc.add(5);
calc.add(3);
calc.subtract(2);
console.log(calc.result); // 6
EOF
npx tsc --noEmit thistype.ts
# (no errors)
# ============================================
# PART 2: UPPERCASE, LOWERCASE
# ============================================
cat > case-basic.ts << 'EOF'
type A = Uppercase<'hello'>; // 'HELLO'
type B = Lowercase<'HELLO'>; // 'hello'
type C = Capitalize<'hello'>; // 'Hello'
type D = Uncapitalize<'Hello'>; // 'hello'
const a: A = 'HELLO';
const b: B = 'hello';
const c: C = 'Hello';
const d: D = 'hello';
// On unions
type E = Uppercase<'a' | 'b' | 'c'>;
// 'A' | 'B' | 'C'
const e: E = 'A';
console.log(a, b, c, d, e);
EOF
npx tsc --noEmit case-basic.ts
# (no errors)
# ============================================
# PART 3: GETTER NAMES
# ============================================
cat > getters.ts << 'EOF'
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
interface User {
name: string;
age: number;
}
type UserGetters = Getters<User>;
const g: UserGetters = {
getName: () => 'Alice',
getAge: () => 30
};
console.log(g.getName(), g.getAge());
EOF
npx tsc --noEmit getters.ts
# (no errors)
# ============================================
# PART 4: EVENT HANDLERS
# ============================================
cat > events.ts << 'EOF'
type Events = 'click' | 'focus' | 'blur';
type Handlers = {
[K in Events as `on${Capitalize<K>}`]: (e: Event) => void;
};
const h: Partial<Handlers> = {
onClick: e => console.log('click'),
onFocus: e => console.log('focus')
};
console.log(Object.keys(h));
EOF
npx tsc --noEmit events.ts
# (no errors)
# ============================================
# PART 5: REVERSE EXTRACTION
# ============================================
cat > reverse.ts << 'EOF'
type GetterKey<S> =
S extends `get${infer K}` ? Uncapitalize<K> : never;
type A = GetterKey<'getName'>; // 'name'
type B = GetterKey<'getEmail'>; // 'email'
type C = GetterKey<'version'>; // never
const a: A = 'name';
const b: B = 'email';
// const c: C = 'anything'; // โ never
console.log(a, b);
EOF
npx tsc --noEmit reverse.ts
# (no errors)
# ============================================
# PART 6: CASE-INSENSITIVE LOOKUP
# ============================================
cat > case-insensitive.ts << 'EOF'
type CaseInsensitiveKeys<T, K extends string> = {
[P in keyof T as Lowercase<string & P> extends Lowercase<K> ? P : never]: T[P];
};
interface Settings {
Theme: string;
Language: string;
}
type ThemeKey = CaseInsensitiveKeys<Settings, 'theme'>;
// { Theme: string }
const t: ThemeKey = { Theme: 'dark' };
console.log(t);
EOF
npx tsc --noEmit case-insensitive.ts
# (no errors)
# ============================================
# PART 7: COMBINED
# ============================================
cat > combined.ts << 'EOF'
interface Entity {
id: number;
createdAt: Date;
}
type Serializable<T> = T & ThisType<T & {
serialize: () => string;
}>;
function makeSerializable<T extends object>(data: T): Serializable<T> {
return {
...data,
serialize() {
return JSON.stringify(this);
}
} as Serializable<T>;
}
const user = makeSerializable({ id: 1, name: 'Alice' });
console.log(user.serialize());
// Case helpers
type Accessors<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
} & {
[K in keyof T as `set${Capitalize<string & K>}`]: (v: T[K]) => void;
};
type UserAccessors = Accessors<{ name: string; age: number }>;
const accessors: UserAccessors = {
getName: () => 'Alice',
setName: (v) => {},
getAge: () => 30,
setAge: (v) => {}
};
console.log(accessors.getName());
EOF
npx tsc --noEmit combined.ts
# (no errors)
# ============================================
# PART 8: COMPILE AND RUN
# ============================================
npx tsc thistype.ts case-basic.ts getters.ts events.ts reverse.ts case-insensitive.ts combined.ts
node thistype.js
# [ 6 ]
node case-basic.js
# [ HELLO hello Hello hello A ]
node getters.js
# [ Alice 30 ]
node events.js
# [ [ 'onClick', 'onFocus' ] ]
node reverse.js
# [ name email ]
node case-insensitive.js
# [ { Theme: 'dark' } ]
node combined.js
# [ {"id":1,"name":"Alice"} ]
# [ Alice ]
Quick Reference
ThisType<T>
| Aspect | Detail |
|---|---|
| Purpose | Set this in object literal methods |
| Form | Type & ThisType<Context> |
| Runtime | None (marker only) |
| Use case | Object factories, frameworks |
| Applies to | Object literals |
Case Helpers
| Helper | Transforms |
|---|---|
Uppercase<T> | 'abc' โ 'ABC' |
Lowercase<T> | 'ABC' โ 'abc' |
Capitalize<T> | 'abc' โ 'Abc' |
Uncapitalize<T> | 'Abc' โ 'abc' |
Case Helpers on Types
| Input | Result |
|---|---|
'hello' | Transformed literal |
string | string |
| Union of literals | Union of transformed |
number | Unchanged |
never | never |
ThisType Usage
| Form | Meaning |
|---|---|
T & ThisType<Ctx> | Object with this: Ctx in methods |
type X = Shape & ThisType<Ctx> | Named pattern |
interface X extends ThisType<Ctx> | Rare |
ThisType<Ctx> alone | Useless |
Common Case Patterns
| Pattern | Code |
|---|---|
| Getter name | `get${Capitalize<K>}` |
| Setter name | `set${Capitalize<K>}` |
| Handler name | `on${Capitalize<K>}` |
| Reverse getter | S extends `get${infer R}` ? Uncapitalize<R> : S |
| Uppercase keys | Uppercase<string & K> |
| Case-insensitive | Lowercase<A> extends Lowercase<B> |
string & K Idiom
| Context | Why |
|---|---|
| Mapped type key | K may be string | number | symbol |
Capitalize<string & K> | Helper requires a string |
| Without it | Compile error |
When to Use ThisType
| Situation | Use ThisType |
|---|---|
| Class-based code | โ (native this) |
| Object factory with shared state | โ |
| Vue Options API style | โ |
| Simple object literal | โ |
| Anonymous object builder | โ |
When to Use Case Helpers
| Situation | Use |
|---|---|
| Generate names | โ |
| Reverse names | โ |
| Case-insensitive match | โ |
| Validate case | โ
(S extends Uppercase<S>) |
Transform a string | โ ๏ธ (result is string) |
| Simple literal | โ |
Combined Patterns
| Pattern | Code |
|---|---|
| Accessors | Getters<T> & Setters<T> |
| Prefix strip | K extends \pre${infer R}` ? Uncapitalize : K` |
| Prefix add | `pre${Capitalize<K>}` |
| Case-insensitive key | Lowercase<string & K> extends Lowercase<Search> |
Error Cases
| Error | Cause |
|---|---|
ThisType is not a normal type | Used where not allowed |
Type X not assignable | Wrong this in method |
Helper argument must be string | Non-string to case helper |
K is not a string | Missing string & K |
Relation to Other Utilities
| Utility | Category |
|---|---|
ThisType | this marker |
Uppercase / Lowercase | String transformation |
Capitalize / Uncapitalize | String transformation |
ReturnType / Parameters | Function extraction |
Partial / Pick / Record | Object transformation |
Key Facts
| Fact | Detail |
|---|---|
ThisType runtime | None โ marker only |
| Case helpers runtime | None โ compile-time only |
| Where compiled | In the type checker |
| Intrinsic? | Case helpers are intrinsic |
| Applied to objects | ThisType only |
| Applied to strings | Case helpers only |
Best Practices
โ Do This:
// Use ThisType for object factory methods
type Ctx = { count: number } & ThisType<{ count: number }>; // โ
// Use Capitalize to generate names
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
}; // โ
// Use Uncapitalize to reverse names
type Key<S> = S extends `get${infer R}` ? Uncapitalize<R> : never; // โ
// Use Lowercase for case-insensitive comparison
type CI<A extends string, B extends string> =
Lowercase<A> extends Lowercase<B> ? true : false; // โ
// Use Uppercase to assert uppercase
type IsUpper<S extends string> = S extends Uppercase<S> ? true : false; // โ
// Always use `string & K` in templates
`get${Capitalize<string & K>}` // โ
// Combine case helpers with template literals
type Handler<E extends string> = `on${Capitalize<E>}`; // โ
// Use ThisType in the intersection form
type Builder = Shape & ThisType<Shape>; // โ
โ Don’t Do This:
// Don't use ThisType alone
type Bad = ThisType<{ x: number }>; // โ ๏ธ useless // โ ๏ธ
// Don't expect ThisType to work in classes
class C { } // this is typed natively โ ThisType ignored // โ ๏ธ
// Don't forget `string & K`
type Bad<T> = {
[K in keyof T as `get${Capitalize<K>}`]: () => T[K];
};
// โ ๏ธ K may not be a string // โ ๏ธ
// Don't use case helpers on objects
type Bad = Uppercase<{ a: 1 }>; // โ ๏ธ unchanged // โ ๏ธ
// Don't expect a `string` input to produce a literal
type Bad = Uppercase<string>; // 'string', not a literal // โ ๏ธ
// Don't rely on case helpers at runtime
// They're compile-time only // โ ๏ธ
// Don't skip ThisType in object factories
type Bad = { add: () => void }; // โ ๏ธ this is the object itself // โ ๏ธ
// Don't overuse case helpers
// A plain union is often clearer // โ ๏ธ
Common Pitfalls
| Pitfall | Problem | Solution |
|---|---|---|
ThisType alone | Useless | Use with an intersection |
ThisType in classes | Ignored | Classes type this natively |
Missing string & K | Compile error | Add it |
| Case helper on object | No effect | Use on strings |
Case helper on string | Result is string | Only works on literals |
| Expecting runtime | Compile-time only | It’s type-level |
Circular this reference | Complex type | Break the cycle |
Uncapitalize on non-prefix | Wrong result | Match pattern first |
Real-World Examples
1. Basic ThisType
type Ctx = { n: number } & ThisType<{ n: number }>;
2. Calculator object
function makeCalc(): { n: number; add: (x: number) => void } & ThisType<{ n: number }> {
return {
n: 0,
add(x) { this.n += x; }
};
}
3. Uppercase union
type A = Uppercase<'a' | 'b'>; // 'A' | 'B'
4. Lowercase comparison
type Equal<A extends string, B extends string> =
Lowercase<A> extends Lowercase<B> ? true : false;
5. Capitalize for getters
type G<T> = { [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K] };
6. Uncapitalize for setters
type S<T> = { [K in keyof T as `set${Capitalize<string & K>}`]: (v: T[K]) => void };
7. Reverse a getter
type Key<S> = S extends `get${infer R}` ? Uncapitalize<R> : never;
8. Event handlers
type H = { [K in 'click' | 'focus' as `on${Capitalize<K>}`]: () => void };
9. Camel case
type Camel<S extends string> =
S extends `${infer H}_${infer R}`
? `${H}${Capitalize<Camel<R>>}`
: S;
10. Kebab to camel
type KebabToCamel<S extends string> =
S extends `${infer A}-${infer B}`
? `${A}${Capitalize<B>}`
: S;
11. Assert uppercase
type IsUpper<S extends string> = S extends Uppercase<S> ? true : false;
12. Assert lowercase
type IsLower<S extends string> = S extends Lowercase<S> ? true : false;
13. Case-insensitive key lookup
type Find<T, Search extends string> = {
[K in keyof T as Lowercase<string & K> extends Lowercase<Search> ? K : never]: T[K];
};
14. Prefix add
type Prefix<T, P extends string> = {
[K in keyof T as `${P}${string & K}`]: T[K];
};
15. Prefix strip
type Strip<T, P extends string> = {
[K in keyof T as K extends `${P}${infer R}` ? Uncapitalize<R> : K]: T[K];
};
16. ThisType with setters
type Store = { value: number } & ThisType<{ value: number; set: (n: number) => void }>;
17. Setter/getter pair
type Pair<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
} & {
[K in keyof T as `set${Capitalize<string & K>}`]: (v: T[K]) => void;
};
18. Uppercase key union
type UpperKeys<T> = { [K in keyof T as Uppercase<string & K>]: T[K] };
19. Lowercase key union
type LowerKeys<T> = { [K in keyof T as Lowercase<string & K>]: T[K] };
20. Event name type
type EventName<E extends string> = `on${Capitalize<E>}`;
Visual: ThisType
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Without ThisType: โ
โ โ
โ const obj = { โ
โ n: 0, โ
โ add() { โ
โ this.n++; โ
โ // this is inferred from the literal โ
โ } โ
โ }; โ
โ โ
โ โ this has the literal's type โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ With ThisType: โ
โ โ
โ type Obj = { โ
โ n: number; โ
โ add: () => void; โ
โ } & ThisType<{ n: number }>; โ
โ โ
โ const obj: Obj = { โ
โ n: 0, โ
โ add() { โ
โ this.n++; // this: { n: number } โ
โ } โ
โ }; โ
โ โ
โ โ this is typed as the context โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: Case Helpers
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 'hello' โ
โ โ โโ โโโโบ Uppercase โ 'HELLO' โ
โ โโโโบ Capitalize โ 'Hello' โ
โ โโโโบ Lowercase โ 'hello' โ
โ โโโโบ Uncapitalize โ 'hello' โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 'HELLO' โ
โ โ โ
โ โโโโบ Uppercase โ 'HELLO' โ
โ โโโโบ Lowercase โ 'hello' โ
โ โโโโบ Capitalize โ 'HELLO' โ
โ โโโโบ Uncapitalize โ 'hELLO' โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: Getter Generation
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ interface User { โ
โ name: string; โ
โ age: number; โ
โ } โ
โ โ
โ type G<T> = { โ
โ [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];โ
โ }; โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ
โ map each key
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ 'name' โ 'getName' โ () => string โ
โ 'age' โ 'getAge' โ () => number โ
โ โ
โ Result: โ
โ { โ
โ getName: () => string; โ
โ getAge: () => number; โ
โ } โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: Reverse Extraction
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ type Key<S> = โ
โ S extends `get${infer R}` โ
โ ? Uncapitalize<R> โ
โ : never; โ
โ โ
โ Key<'getName'> โ
โ โ โ
โ โผ โ
โ R = 'Name' โ
โ Uncapitalize<'Name'> = 'name' โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: Case-Insensitive Comparison
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ type Equal<A, B> = โ
โ Lowercase<A> extends Lowercase<B> โ
โ ? true โ
โ : false; โ
โ โ
โ Equal<'Hello', 'hello'> โ
โ โ โ
โ โผ โ
โ Lowercase<'Hello'> = 'hello' โ
โ Lowercase<'hello'> = 'hello' โ
โ 'hello' extends 'hello' โ true โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: string & K Idiom
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ K = 'name' โ
โ string & K = 'name' โ
โ Capitalize<'name'> = 'Name' โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ K = number (from an array's keyof) โ
โ string & K = never โ
โ Capitalize<never> = never โ
โ โ key is dropped โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Without `string &`: โ
โ โ
โ Capitalize<K> where K = number โ
โ โ compile error โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: Object Factory with ThisType
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ type Ctx = { count: number }; โ
โ type Counter = Ctx & ThisType<Ctx & { โ
โ inc: () => void; โ
โ }>; โ
โ โ
โ function make(): Counter { โ
โ return { โ
โ count: 0, โ
โ inc() { โ
โ this.count++; โ
โ } โ
โ }; โ
โ } โ
โ โ
โ const c = make(); โ
โ c.inc(); โ
โ c.count; // 1 โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: Combined Mapped Types
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ type Accessors<T> = โ
โ { โ
โ [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];โ
โ } & โ
โ { โ
โ [K in keyof T as `set${Capitalize<string & K>}`]: (v: T[K]) => void;โ
โ }; โ
โ โ
โ Accessors<{ name: string }> โ
โ โ โ
โ โผ โ
โ { โ
โ getName: () => string; โ
โ setName: (v: string) => void; โ
โ } โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: When to Use Each
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Typing `this` in object literals? โ
โ โโโ ThisType<T> โ
โ โ
โ Need string case transformation? โ
โ โโโ Uppercase / Lowercase / โ
โ Capitalize / Uncapitalize โ
โ โ
โ Generating names from keys? โ
โ โโโ Case helpers in template literals โ
โ โ
โ Reversing generated names? โ
โ โโโ infer + Uncapitalize โ
โ โ
โ Case-insensitive matching? โ
โ โโโ Lowercase on both sides โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: Distribution
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Uppercase<'a' | 'b' | 'c'> โ
โ โ โ
โ โ distribute โ
โ โผ โ
โ 'A' | 'B' | 'C' โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Capitalize<'name' | 'age'> โ
โ โ โ
โ โผ โ
โ 'Name' | 'Age' โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Uncapitalize<'Name' | 'Age'> โ
โ โ โ
โ โผ โ
โ 'name' | 'age' โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Visual: Decision Flow
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Need `this` typed in an object literal? โ
โ โโโ ThisType<T> โ
โ โ
โ Transform string case? โ
โ โโโ Uppercase โโโบ ALL CAPS โ
โ โโโ Lowercase โโโบ all lowercase โ
โ โโโ Capitalize โโโบ First capital โ
โ โโโ Uncapitalize โโโบ first lowercase โ
โ โ
โ Generating names? โ
โ โโโ Case helper in template literal โ
โ โ
โ Reversing names? โ
โ โโโ infer + Uncapitalize โ
โ โ
โ Case-insensitive match? โ
โ โโโ Lowercase both sides โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Summary
| Utility | Purpose |
|---|---|
ThisType<T> | Set this in object literal methods |
Uppercase<T> | Transform to uppercase |
Lowercase<T> | Transform to lowercase |
Capitalize<T> | Capitalize first letter |
Uncapitalize<T> | Lowercase first letter |
Key takeaways:
ThisType<T>is a marker that typesthisin object literal methods- It only works in intersections with object types โ
Shape & ThisType<Ctx> - It’s a compile-time marker โ no runtime footprint
- Common use: object factories that build methods sharing state
- The four case helpers are intrinsic โ the compiler computes them
Uppercaseโ ALL CAPS;Lowercaseโ all lowercaseCapitalizeโ First capital;Uncapitalizeโ first lowercase- They work on string literal types, distribute over unions, and pass through non-strings
- On the general
stringtype, the result isstringโ no literal to transform string & Knarrows a mapped key to a string for the helper- Common pattern โ
`get${Capitalize<string & K>}`โ generates getter names - Reverse pattern โ
S extends `get${infer R}` ? Uncapitalize<R> : Sโ strips prefixes - Case-insensitive comparison โ
Lowercase<A> extends Lowercase<B> - Both features are niche but essential โ
ThisTypefor library-style object factories, case helpers for name generation
Remember: ThisType<T> solves a specific problem โ typing this inside object literal methods, which classes handle natively. The case helpers solve another โ transforming string types. Both are specialized; both matter where they apply. ThisType shows up in libraries and frameworks that build objects dynamically. The case helpers power every mapped-type pattern that generates or reverses names. Together they complete the standard library’s utility set: object shapes, unions, functions, this, and strings โ each with its own toolkit.
Stop using slow, ad-bloated tool sites! ๐คฎ
๐ Search “KandZ Tools” on Google to use many professional utilities for free.
KandZ.me is the ultimate minimalist hub for:
โ
Finance (Mortgage, Interest, Inflation)
โ
Tech (Base64, JSON, Dev Suite, IP)
โ
Health (BMI, BMR, TDEE)
โ
Productivity (Timer, Workspace, QR)
โก๏ธ Fast & Private
๐ No data leaves your device
๐ 100% Free
๐ Use it now: https://tools.kandz.me
๐ Bookmark itโyouโll need it later!