Пойми, как гомоморфные сопоставленные типы сохраняют модификаторы и как выражение as переименовывает или отфильтровывает ключи.
Открыть этот урок в KodokonСопоставленный тип перебирает набор ключей, чтобы получить новый объектный тип. Форма [K in keyof T] называется гомоморфной: компилятор знает, что структура пришла из T, и автоматически копирует модификаторы readonly и ? каждого исходного свойства. Префиксы + и - позволяют явно добавить или убрать эти модификаторы: именно так внутри устроены Partial, Required и 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 }Начиная с TypeScript 4.1 выражение as включает переименование ключей: каждый ключ можно переименовать на лету, обычно через шаблонный литеральный тип. А если выражение даёт never, ключ просто удаляется из результата: это официальный механизм фильтрации свойств. Обрати внимание на & string в примере: keyof T может содержать ключи типа symbol или number, несовместимые с шаблонным литералом.
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];
};Тонкость спецификации, о которой часто забывают: применённый к массиву или кортежу гомоморфный сопоставленный тип не даёт объект с ключами 0, 1 и length. Компилятор сохраняет природу типа: он преобразует элементы и оставляет форму массива или кортежа. Именно поэтому Readonly<[number, string]> остаётся кортежем. Это сохранение исчезает, как только сопоставленный тип перестаёт быть гомоморфным - например, при [P in K], где K является произвольным объединением ключей.
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 даёт never для ключа?{ readonly [K in keyof T]: T[K] }, применённый к кортежу [number, string]?-? в сопоставленном типе?