Kodokon kodokon.com

Сопоставленные типы и переименование ключей: преобразуем типы

Пойми, как гомоморфные сопоставленные типы сохраняют модификаторы и как выражение as переименовывает или отфильтровывает ключи.

8 мин · 3 вопросов

Открыть этот урок в Kodokon

Сопоставленный тип перебирает набор ключей, чтобы получить новый объектный тип. Форма [K in keyof T] называется гомоморфной: компилятор знает, что структура пришла из T, и автоматически копирует модификаторы readonly и ? каждого исходного свойства. Префиксы + и - позволяют явно добавить или убрать эти модификаторы: именно так внутри устроены Partial, Required и Readonly.

TYPESCRIPT
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 }
Префиксы - убирают readonly и необязательность.

Начиная с TypeScript 4.1 выражение as включает переименование ключей: каждый ключ можно переименовать на лету, обычно через шаблонный литеральный тип. А если выражение даёт never, ключ просто удаляется из результата: это официальный механизм фильтрации свойств. Обрати внимание на & string в примере: keyof T может содержать ключи типа symbol или number, несовместимые с шаблонным литералом.

TYPESCRIPT
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];
};
Переименование через as, фильтрация через never.

Тонкость спецификации, о которой часто забывают: применённый к массиву или кортежу гомоморфный сопоставленный тип не даёт объект с ключами 0, 1 и length. Компилятор сохраняет природу типа: он преобразует элементы и оставляет форму массива или кортежа. Именно поэтому Readonly<[number, string]> остаётся кортежем. Это сохранение исчезает, как только сопоставленный тип перестаёт быть гомоморфным - например, при [P in K], где K является произвольным объединением ключей.

TYPESCRIPT
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[]
Гомоморфный сопоставленный тип уважает массивы и кортежи.

Проверка знаний

Убедись, что запомнил ключевые моменты этого урока.

  1. Что происходит при переименовании ключей, когда выражение as даёт never для ключа?
    • Свойство получает тип never
    • Ключ удаляется из итогового типа
    • Компилятор выдаёт ошибку
  2. Что даёт { readonly [K in keyof T]: T[K] }, применённый к кортежу [number, string]?
    • Объект с ключами 0, 1 и length
    • readonly [number, string]
    • readonly (number | string)[]
    • Ошибку компиляции
  3. Каков эффект модификатора -? в сопоставленном типе?
    • Он делает свойство необязательным
    • Он убирает необязательность и добавленный ею undefined
    • Он убирает модификатор readonly
    • Он удаляет свойство из типа