TypeScript Mapped Types: [K in keyof T], readonly and Key Remapping with as
November 6, 2026
Welcome to episode 41 of TypeScript from Zero, a series for complete beginners (no JavaScript needed). Welcome back to TypeScript from zero! In episode thirty two we used Partial and Readonly. But how are they made? With a mapped type: a loop over the keys of a type. Today we write our own. A new file called mapped.ts in the tasks project. Every code block below is the exact code and output from the video, ready to copy.
- All episodes are in the TypeScript from Zero playlist.
- Versions used: Node.js 26.13.0, npm 11.20.0, TypeScript 7.0.2.
Setup
We start in the terminal and open the project in VS Code:
cd tasks
touch src/mapped.ts
code .1. Mapped type
The syntax looks like an object with one special key. Square brackets, K in key of T, means: for every key K of T. The value is T indexed by K, the same type as before. With a question mark after the brackets, every property becomes optional. This is exactly how Partial works. So an object with only dark is fine.
src/mapped.ts:
type Flags = { dark: boolean; compact: boolean };
type Optional<T> = { [K in keyof T]?: T[K] };
const some: Optional<Flags> = { dark: true };
console.log(some);$ node src/mapped.ts
{ dark: true }2. Modifiers
We can change the modifiers. With the word readonly before the brackets, every property is read only. Assigning to one of them is an error: cannot assign to dark, because it is a read only property. TS2540.
src/mapped.ts:
type Flags = { dark: boolean; compact: boolean };
type Optional<T> = { [K in keyof T]?: T[K] };
const some: Optional<Flags> = { dark: true };
console.log(some);
type Frozen<T> = { readonly [K in keyof T]: T[K] };
const fixed: Frozen<Flags> = { dark: true, compact: false };
fixed.dark = false;$ npx tsc --noEmit
src/mapped.ts:7:7 - error TS2540: Cannot assign to 'dark' because it is a read-only property.
7 fixed.dark = false;
~~~~
Found 1 error in src/mapped.ts:73. Fix
We delete the assignment.
src/mapped.ts:
type Flags = { dark: boolean; compact: boolean };
type Optional<T> = { [K in keyof T]?: T[K] };
const some: Optional<Flags> = { dark: true };
console.log(some);
type Frozen<T> = { readonly [K in keyof T]: T[K] };
const fixed: Frozen<Flags> = { dark: true, compact: false };$ npx tsc --noEmit4. Remapping
A mapped type can also rename the keys, with the word as. Here every key becomes get plus the name with a capital letter, using a template literal type and Capitalize. And every value becomes a function that returns the old value type. So dark turns into get dark, a function that returns a boolean. But the object that we wrote has only get dark. tsc says: property get compact is missing. TS2741.
src/mapped.ts:
type Flags = { dark: boolean; compact: boolean };
type Optional<T> = { [K in keyof T]?: T[K] };
const some: Optional<Flags> = { dark: true };
console.log(some);
type Frozen<T> = { readonly [K in keyof T]: T[K] };
const fixed: Frozen<Flags> = { dark: true, compact: false };
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
const g: Getters<Flags> = { getDark: () => true };
console.log(g.getDark());$ npx tsc --noEmit
src/mapped.ts:10:7 - error TS2741: Property 'getCompact' is missing in type '{ getDark: () => true; }' but required in type 'Getters<Flags>'.
10 const g: Getters<Flags> = { getDark: () => true };
~
Found 1 error in src/mapped.ts:105. Fix
We add the missing function, and the program runs.
src/mapped.ts:
type Flags = { dark: boolean; compact: boolean };
type Optional<T> = { [K in keyof T]?: T[K] };
const some: Optional<Flags> = { dark: true };
console.log(some);
type Frozen<T> = { readonly [K in keyof T]: T[K] };
const fixed: Frozen<Flags> = { dark: true, compact: false };
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
const g: Getters<Flags> = { getDark: () => true, getCompact: () => false };
console.log(g.getDark());$ node src/mapped.ts
{ dark: true }
true$ npx tsc --noEmitRecap
Today: mapped types with K in key of T, the question mark and readonly modifiers, and renaming keys with as. Next episode: conditional types and infer.