Understand how homomorphic mapped types preserve modifiers and how the as clause renames or filters keys.
Open this lesson in KodokonA mapped type iterates over a set of keys to produce a new object type. The [K in keyof T] form is called homomorphic: the compiler knows the structure comes from T and automatically copies the readonly and ? modifiers of each original property. The + and - prefixes let you explicitly add or remove these modifiers: this is exactly the internal machinery behind Partial, Required, and Readonly.
type Mutable<T> = {
-readonly [K in keyof T]-?: T[K];
};
interface Config {
readonly host?: string;
readonly port?: number;
}
type Draft = Mutable<Config>;
// { host: string; port: number }Since TypeScript 4.1, the as clause enables key remapping: each key can be renamed on the fly, typically through a template literal type. And if the clause produces never, the key is simply removed from the result: this is the official property-filtering mechanism. Note the & string in the example: keyof T may contain symbol or number keys, which are incompatible with a template literal.
type Getters<T> = {
[K in keyof T & string as `get${Capitalize<K>}`]:
() => T[K];
};
interface User { name: string; age: number }
type UserGetters = Getters<User>;
// { getName: () => string; getAge: () => number }
type OnlyMethods<T> = {
[K in keyof T as
T[K] extends (...args: never[]) => unknown
? K : never]: T[K];
};A specification subtlety that is often overlooked: applied to an array or a tuple, a homomorphic mapped type does not produce an object with the keys 0, 1, and length. The compiler preserves the nature of the type: it transforms the elements and keeps the array or tuple shape. This is what allows Readonly<[number, string]> to remain a tuple. That preservation disappears as soon as the mapped type is no longer homomorphic, for example with [P in K] where K is an arbitrary union of keys.
type Immutable<T> = {
readonly [K in keyof T]: T[K];
};
type Pair = Immutable<[number, string]>;
// readonly [number, string]: the tuple survives
type List = Immutable<string[]>;
// readonly string[]as clause produces never for a key?{ readonly [K in keyof T]: T[K] } produce when applied to the tuple [number, string]?-? modifier in a mapped type?