Designs complex generic types, refactors `any` types to strict alternatives, creates type guards and utility types, and resolves TypeScript compiler errors. Use when the user asks about TypeScript (TS) types, generics, type inference, type guards, removing `any` types, strict typing, type errors, `infer`, `extends`, conditional types, mapped types, template literal types, branded/opaque types, or utility types like `Partial`, `Record`, `ReturnType`, and `Awaited`.
97
97%
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Passed
No known issues
Mapped types allow you to create new types by transforming each property of an existing type. They iterate over keys and apply transformations to create new type structures.
type MappedType<T> = {
[K in keyof T]: TransformedType;
};type MyPartial<T> = {
[K in keyof T]?: T[K];
};
interface User {
id: string;
name: string;
email: string;
}
type PartialUser = MyPartial<User>;
// { id?: string; name?: string; email?: string }type MyRequired<T> = {
[K in keyof T]-?: T[K]; // -? removes optional modifier
};type MyReadonly<T> = {
readonly [K in keyof T]: T[K];
};type Mutable<T> = {
-readonly [K in keyof T]: T[K]; // -readonly removes readonly modifier
};When you just iterate over keyof T, you preserve the original keys:
type Preserve<T> = {
[K in keyof T]: T[K]; // Same type, just recreated
};asTransform keys while mapping using the as clause:
type RemapKeys<T> = {
[K in keyof T as NewKeyType]: T[K];
};type Prefixed<T, P extends string> = {
[K in keyof T as K extends string ? `${P}${K}` : K]: T[K];
};
interface User {
name: string;
age: number;
}
type PrefixedUser = Prefixed<User, "user_">;
// { user_name: string; user_age: number }nevertype RemoveFields<T, K extends keyof T> = {
[P in keyof T as P extends K ? never : P]: T[P];
};
type UserWithoutEmail = RemoveFields<User, "email">;
// { id: string; name: string }type RemoveMapsPrefixFromObj<T> = {
[K in keyof T as RemoveMaps<K>]: T[K];
};
type RemoveMaps<T> = T extends `maps:${infer Rest}` ? Rest : T;
interface ApiData {
"maps:longitude": string;
"maps:latitude": string;
}
type CleanData = RemoveMapsPrefixFromObj<ApiData>;
// { longitude: string; latitude: string }Use conditional types in the as clause to filter:
// Only keep string properties
type OnlyStrings<T> = {
[K in keyof T as T[K] extends string ? K : never]: T[K];
};
interface Mixed {
name: string;
age: number;
email: string;
active: boolean;
}
type StringProps = OnlyStrings<Mixed>;
// { name: string; email: string }type RequiredKeys<T> = {
[K in keyof T]-?: undefined extends T[K] ? never : K;
}[keyof T];
type OnlyRequired<T> = Pick<T, RequiredKeys<T>>;type OptionalKeys<T> = {
[K in keyof T]-?: undefined extends T[K] ? K : never;
}[keyof T];
type OnlyOptional<T> = Pick<T, OptionalKeys<T>>;type Promisify<T> = {
[K in keyof T]: Promise<T[K]>;
};
interface SyncApi {
getUser(): User;
getPost(): Post;
}
type AsyncApi = Promisify<SyncApi>;
// { getUser: Promise<() => User>; getPost: Promise<() => Post> }type Arrayify<T> = {
[K in keyof T]: T[K][];
};
interface Single {
name: string;
count: number;
}
type Multiple = Arrayify<Single>;
// { name: string[]; count: number[] }type Nullable<T> = {
[K in keyof T]: T[K] | null;
};Apply transformations recursively:
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object
? DeepReadonly<T[K]>
: T[K];
};
interface Nested {
user: {
profile: {
name: string;
};
};
}
type ReadonlyNested = DeepReadonly<Nested>;
// All levels are readonlytype DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
};type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K];
};
type Setters<T> = {
[K in keyof T as `set${Capitalize<string & K>}`]: (value: T[K]) => void;
};
interface Person {
name: string;
age: number;
}
type PersonGetters = Getters<Person>;
// { getName: () => string; getAge: () => number }
type PersonSetters = Setters<Person>;
// { setName: (value: string) => void; setAge: (value: number) => void }type EventHandlers<T> = {
[K in keyof T as `on${Capitalize<string & K>}Change`]: (
newValue: T[K],
oldValue: T[K]
) => void;
};
interface State {
count: number;
name: string;
}
type StateHandlers = EventHandlers<State>;
// {
// onCountChange: (newValue: number, oldValue: number) => void;
// onNameChange: (newValue: string, oldValue: string) => void;
// }type ValidationErrors<T> = {
[K in keyof T]?: string[];
};
interface RegistrationForm {
email: string;
password: string;
confirmPassword: string;
}
type RegistrationErrors = ValidationErrors<RegistrationForm>;
// { email?: string[]; password?: string[]; confirmPassword?: string[] }type PickAndTransform<T, K extends keyof T> = {
[P in K]: T[P] extends Function ? T[P] : Readonly<T[P]>;
};type Merge<A, B> = {
[K in keyof A | keyof B]: K extends keyof B
? B[K]
: K extends keyof A
? A[K]
: never;
};// Create index signature from union
type FromUnion<K extends string, V> = {
[P in K]: V;
};
type Dict = FromUnion<"a" | "b" | "c", number>;
// { a: number; b: number; c: number }Template literals require string keys:
// Error: Type 'K' is not assignable to type 'string'
type Wrong<T> = {
[K in keyof T as `prefix_${K}`]: T[K];
};
// Correct: Check K extends string
type Correct<T> = {
[K in keyof T as K extends string ? `prefix_${K}` : never]: T[K];
};Remapping can lose optional/readonly modifiers:
// Original optional modifier lost
type Transform<T> = {
[K in keyof T as `new_${string & K}`]: T[K];
};
// Preserve optional with conditional
type TransformPreserve<T> = {
[K in keyof T as `new_${string & K}`]+?: T[K];
};Deep mapped types can cause issues:
// Potential infinite recursion with circular types
type DeepReadonly<T> = {
readonly [K in keyof T]: DeepReadonly<T[K]>;
};
// Add base case for primitives
type DeepReadonlySafe<T> = T extends object
? { readonly [K in keyof T]: DeepReadonlySafe<T[K]> }
: T;