This TypeScript error occurs when creating mapped types with incorrect syntax. Mapped types require the "in" keyword to iterate over union types, and this error appears when the type parameter is missing or malformed. The fix involves correcting the mapped type syntax to use proper "key in Type" pattern.
The "Type parameter for mapped type must be 'in' expression" error occurs when TypeScript encounters invalid syntax in a mapped type declaration. Mapped types are a powerful TypeScript feature that allows you to create new types by transforming properties of existing types. In TypeScript, mapped types use the syntax `{[K in KeyType]: ValueType}` where `K` is a type variable that iterates over each member of `KeyType` (typically a union type). The `in` keyword is required to specify that you're mapping over the keys. This error typically appears when: 1. You forget the `in` keyword entirely 2. You use incorrect syntax like `{[K]: ValueType}` without `in` 3. You have a typo or misplaced syntax in the type parameter 4. You're trying to use a conditional type or other expression where mapped type syntax is expected The error prevents compilation because TypeScript cannot parse the mapped type declaration correctly.
The most common fix is to add the in keyword between the type parameter and the key type:
// WRONG - missing 'in'
type Partial<T> = {
[K]?: T[K]; // Error: Type parameter for mapped type must be 'in' expression
};
// CORRECT - add 'in'
type Partial<T> = {
[K in keyof T]?: T[K]; // ✓ Correct syntax
};
// Another example:
// WRONG
type ReadonlyProps<T> = {
[P]: T[P]; // Error
};
// CORRECT
type ReadonlyProps<T> = {
readonly [P in keyof T]: T[P]; // ✓
};The basic pattern is always: {[TypeParameter in KeyType]: ValueType}
Ensure you're using exactly in (lowercase, two letters):
// WRONG - typo
type Mapped<T> = {
[K inn keyof T]: T[K]; // 'inn' instead of 'in'
};
// WRONG - uppercase
type Mapped<T> = {
[K IN keyof T]: T[K]; // 'IN' instead of 'in'
};
// WRONG - wrong keyword
type Mapped<T> = {
[K of keyof T]: T[K]; // 'of' instead of 'in'
};
// CORRECT
type Mapped<T> = {
[K in keyof T]: T[K]; // ✓
};Also check for missing spaces:
// WRONG - missing space
type Mapped<T> = {[K inkeyof T]: T[K]}; // 'inkeyof' should be 'in keyof'
// CORRECT
type Mapped<T> = {[K in keyof T]: T[K]}; // ✓ Space after 'in'The type after in must be a valid key type (typically a union type or keyof expression):
// WRONG - using a type that isn't valid for iteration
type Colors = 'red' | 'green' | 'blue';
type ColorValues = {
[C in Colors]: string; // ✓ This is actually correct!
};
// WRONG - trying to iterate over non-union type
type BadExample = {
[K in string]: any; // Error: string is too broad for mapped types
};
// CORRECT - use keyof to get keys from an object type
interface User {
id: number;
name: string;
email: string;
}
type OptionalUser = {
[K in keyof User]?: User[K]; // ✓ Makes all properties optional
};
// CORRECT - iterate over literal union
type Status = 'pending' | 'success' | 'error';
type StatusMap = {
[S in Status]: boolean; // ✓ Creates {pending: boolean, success: boolean, error: boolean}
};Common valid key types:
- keyof T (keys of an object type)
- Union of string literals: 'a' | 'b' | 'c'
- Template literal types (TypeScript 4.1+)
Don't confuse mapped types with index signatures. They have different syntax:
// INDEX SIGNATURE (not a mapped type)
interface StringDictionary {
[key: string]: string; // ✓ No 'in', just [key: type]: valueType
}
// MAPPED TYPE (requires 'in')
type ReadonlyDictionary<T> = {
readonly [K in keyof T]: T[K]; // ✓ Uses 'in' keyword
};
// WRONG - mixing syntax
type ConfusedType = {
[key: keyof T]: T[key]; // Error: Should be [K in keyof T]: T[K]
};
// CORRECT - choose one pattern
// Option 1: Index signature (all string keys)
type StringIndex = {
[key: string]: number;
};
// Option 2: Mapped type (specific keys)
type SpecificKeys = {
[K in 'x' | 'y' | 'z']: number;
};Use index signatures when you want all possible string/number keys.
Use mapped types when you want to transform specific known keys.
In complex type definitions, the error might be in a nested position:
// WRONG - error in nested mapped type
type DeepMapped<T> = {
[K in keyof T]: {
[P]: T[K][P]; // Error here! Missing 'in' in nested mapped type
};
};
// CORRECT - fix nested mapped type
type DeepMapped<T> = {
[K in keyof T]: {
[P in keyof T[K]]: T[K][P]; // ✓
};
};
// Another example with conditional types:
// WRONG
type ConditionalMapped<T> = T extends object ? {
[K]: T[K] // Error: missing 'in'
} : never;
// CORRECT
type ConditionalMapped<T> = T extends object ? {
[K in keyof T]: T[K] // ✓
} : never;
// Check template literal types (TypeScript 4.1+):
// WRONG
type TemplateMapped = {
[K in `prefix_${string}`]: K; // ✓ This is actually correct syntax!
// Template literal types work with 'in'
};When debugging, break complex types into smaller parts to isolate the error.
When unsure about mapped type syntax, use the TypeScript Playground to test:
1. Go to [TypeScript Playground](https://www.typescriptlang.org/play)
2. Paste your type definition
3. Check for errors in the editor
4. Compare with working examples:
// Working examples to compare:
// Basic mapped type
type Partial<T> = { [P in keyof T]?: T[P] };
// Mapped type with modifier
type Readonly<T> = { readonly [P in keyof T]: T[P] };
// Mapped type from union
type EventHandlers = { [E in 'click' | 'hover']: () => void };
// Mapped type with conditional value
type Nullable<T> = { [P in keyof T]: T[P] | null };
// Mapped type renaming keys (as clause)
type Getters<T> = {
[P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
};The playground provides instant feedback and helps you understand the correct syntax patterns.
### Mapped Type Modifiers
TypeScript provides built-in modifiers for mapped types:
// Remove optionality ( -? )
type Concrete<T> = {
[P in keyof T]-?: T[P];
};
// Remove readonly ( -readonly )
type Writable<T> = {
-readonly [P in keyof T]: T[P];
};
// Add both readonly and optional
type ReadonlyPartial<T> = {
readonly [P in keyof T]?: T[P];
};### The "as" Clause (Key Remapping)
TypeScript 4.1+ introduced key remapping with the as clause:
// Rename keys
type Getters<T> = {
[P in keyof T as `get${Capitalize<string & P>}`]: () => T[P];
};
// Filter keys
type MethodsOnly<T> = {
[P in keyof T as T[P] extends Function ? P : never]: T[P];
};
// Transform key names with template literals
type ApiEndpoints = {
[K in 'user' | 'product' as `/api/${K}`]: {
get: () => Promise<any>;
post: (data: any) => Promise<any>;
};
};### Mapped Types vs Conditional Types
Don't confuse mapped types with conditional types:
// CONDITIONAL TYPE (uses extends, not in)
type StringOrNumber<T> = T extends string ? string : number;
// MAPPED TYPE (uses in)
type OptionalProps<T> = {
[K in keyof T]?: T[K];
};
// COMBINED: Mapped type with conditional value
type NullableProps<T> = {
[K in keyof T]: T[K] | null;
};### Common Built-in Mapped Types
TypeScript includes several useful mapped types:
// Partial<T> - all properties optional
type Partial<T> = { [P in keyof T]?: T[P] };
// Required<T> - all properties required
type Required<T> = { [P in keyof T]-?: T[P] };
// Readonly<T> - all properties readonly
type Readonly<T> = { readonly [P in keyof T]: T[P] };
// Pick<T, K> - subset of properties
type Pick<T, K extends keyof T> = { [P in K]: T[P] };
// Record<K, T> - object with specific keys
type Record<K extends keyof any, T> = { [P in K]: T };### Performance Considerations
Complex mapped types can slow down TypeScript compilation:
- Very large unions (100+ members) in in position
- Deeply nested mapped types
- Recursive mapped types
Use interface instead of complex mapped types for frequently used types to improve performance.
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript