| |

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 this inside 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 this typing)

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 this typed 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. ThisType supports the dynamic case while keeping this typed.


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 of T. 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 & K and not K as string: string & K is a type operation โ€” it produces the intersection of string and K. When K is a string literal, the intersection is the literal itself. When K is number or symbol, the intersection is never (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:

  • createRequest uses ThisType to type this inside setHeader
  • Getters<T> uses Capitalize in a mapped type to generate get* method names
  • Handlers uses Capitalize to generate on* handlers
  • GetterKey uses Uncapitalize to reverse getName โ†’ name
  • CaseInsensitiveKeys uses Lowercase to 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. ThisType builds objects with typed this; the case helpers derive names. Both are used by real frameworks โ€” Vue’s Options API uses ThisType; 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>

AspectDetail
PurposeSet this in object literal methods
FormType & ThisType<Context>
RuntimeNone (marker only)
Use caseObject factories, frameworks
Applies toObject literals

Case Helpers

HelperTransforms
Uppercase<T>'abc' โ†’ 'ABC'
Lowercase<T>'ABC' โ†’ 'abc'
Capitalize<T>'abc' โ†’ 'Abc'
Uncapitalize<T>'Abc' โ†’ 'abc'

Case Helpers on Types

InputResult
'hello'Transformed literal
stringstring
Union of literalsUnion of transformed
numberUnchanged
nevernever

ThisType Usage

FormMeaning
T & ThisType<Ctx>Object with this: Ctx in methods
type X = Shape & ThisType<Ctx>Named pattern
interface X extends ThisType<Ctx>Rare
ThisType<Ctx> aloneUseless

Common Case Patterns

PatternCode
Getter name`get${Capitalize<K>}`
Setter name`set${Capitalize<K>}`
Handler name`on${Capitalize<K>}`
Reverse getterS extends `get${infer R}` ? Uncapitalize<R> : S
Uppercase keysUppercase<string & K>
Case-insensitiveLowercase<A> extends Lowercase<B>

string & K Idiom

ContextWhy
Mapped type keyK may be string | number | symbol
Capitalize<string & K>Helper requires a string
Without itCompile error

When to Use ThisType

SituationUse 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

SituationUse
Generate namesโœ…
Reverse namesโœ…
Case-insensitive matchโœ…
Validate caseโœ… (S extends Uppercase<S>)
Transform a stringโš ๏ธ (result is string)
Simple literalโŒ

Combined Patterns

PatternCode
AccessorsGetters<T> & Setters<T>
Prefix stripK extends \pre${infer R}` ? Uncapitalize : K`
Prefix add`pre${Capitalize<K>}`
Case-insensitive keyLowercase<string & K> extends Lowercase<Search>

Error Cases

ErrorCause
ThisType is not a normal typeUsed where not allowed
Type X not assignableWrong this in method
Helper argument must be stringNon-string to case helper
K is not a stringMissing string & K

Relation to Other Utilities

UtilityCategory
ThisTypethis marker
Uppercase / LowercaseString transformation
Capitalize / UncapitalizeString transformation
ReturnType / ParametersFunction extraction
Partial / Pick / RecordObject transformation

Key Facts

FactDetail
ThisType runtimeNone โ€” marker only
Case helpers runtimeNone โ€” compile-time only
Where compiledIn the type checker
Intrinsic?Case helpers are intrinsic
Applied to objectsThisType only
Applied to stringsCase 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

PitfallProblemSolution
ThisType aloneUselessUse with an intersection
ThisType in classesIgnoredClasses type this natively
Missing string & KCompile errorAdd it
Case helper on objectNo effectUse on strings
Case helper on stringResult is stringOnly works on literals
Expecting runtimeCompile-time onlyIt’s type-level
Circular this referenceComplex typeBreak the cycle
Uncapitalize on non-prefixWrong resultMatch 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

UtilityPurpose
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 types this in 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 lowercase
  • Capitalize โ†’ First capital; Uncapitalize โ†’ first lowercase
  • They work on string literal types, distribute over unions, and pass through non-strings
  • On the general string type, the result is string โ€” no literal to transform
  • string & K narrows 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 โ€” ThisType for 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!