Я всегда восхищаюсь мощью системы шрифтов в программировании. В последнее время TypeScript очаровал меня своими сверхспособностями.
Сопоставленные типы позволяют создавать новые типы на основе существующих типов. При определении правила для преобразования каждого свойства исходного типа результирующие преобразованные свойства в совокупности образуют новый тип.
Ящик для инструментов
Вот список сопоставителей типов, предоставляемых Typescript из коробки:
// Make all properties of T optional: ? operator // `keyof T` defines a set, we can iterate keys by [Property in keyof T] type Partial<T> = { [Property in keyof T]?: T[Property]; }; type SomePartial = Partial<{ name: string; }> // { name?: string | undefined } // Make all properties of T required: -? operator type Required<T> = { [Property in keyof T]-?: T[Property]; }; type SomeRequired = Required<{ name?: string; }> // { name: string } // Make all properties of T readonly: readonly operator type Readonly<T> = { readonly [Property in keyof T]: T[Property]; }; type SomeReadonly = Readonly<{ name: string; }> // { readonly name: string } // Composed type SomeComposed = Partial<Readonly<{ name: string }>> // { readonly name?: string | undefined }
Бонусный оператор -readonly сопоставляет типы с доступными для записи. (не входит в машинопись)
// Make all properties of T writable: -readonly operator type Writable<T> = { -readonly [Property in keyof T]: T[Property]; }; type SomeWritable = Writable<{ readonly name: string; }> // { name: string }
Typescript 4.1 позволяет использовать переназначение ключей для изменения существующих ключей типа для создания нового.
// As classic map method, we can use `as operator` to map keys. // [Property in (key of T -> as (map) -> NewProperties)]: Iterate mapped keys type ReMapped<T> = { [Property in keyof T as NewProperties]: T[Property] } // Use template literal types to create new keys. type Getter<T> = { [Property in keyof T as `get${Capitalize<string & Property>}`]: () => T[Property] } type SomeGetter = Getter<{ id: string }> // { getId: () => string } // Filter out keys with Exclude type ExcludeName<T> = { [Property in keyof T as Exclude<Property, 'name' | 'id'>]: T[Property] } type SomeNameExcluded = ExcludeName<{ name: string; age: number; id: string }> // { age: number } // Pick keys with Extract type ExtractName<T> = { [Property in keyof T as Extract<Property, 'name' | 'age'>]: T[Property] } type SomeNameExtracted = ExtractName<{ id: string, name: string; age: number }> // { name: string, age: number }
Кроме того, мы можем сопоставлять типы, используя Условные типы, например тернарный оператор.
type StringIdentifiable<Type> = { [Property in keyof Type]: Type[Property] extends { id: string } ? true : false; }; type SomeStringIdentifiable = StringIdentifiable<{ user: { id: number, name: string, age: number }, order: { id: string, amount: number } }> // { user: false; order: true; }
Случаи использования
1. Давайте создадим состояние редуктора, в котором сущности хранятся в нормализованном виде.
// Define entities type Entities = { essay: Essay // {id: string, title: string} user: User // {id: string, name: string} } // Instead of writing this: type State = { essay: Record<Essay['id'], Essay> user: Record<User['id'], User> } // We can define `State` type by mapping `Entities`: // Just add a new entity to `Entities` and it will be added to `State` automatically. type State = { [Property in keyof Entities]: Record<Entities[Property]['id'], Entities[Property]> }
2. Чтобы использовать неизменяемый дизайн, Readonly‹T› полезен для определения сущностей. Однако в тех случаях, когда черновой объект необходим для изменения, можно использовать оператор -readonly для отображения доступного для записи объекта.
type Writable<T> = { -readonly [Property in keyof T]: T[Property] } type User = Readonly<{ id: string; name: string; }> type WritableUser = Writable<User> // { id: string; name: string; }
Смысл
Определения типов дают нам лучшее понимание кодовой базы и позволяют обнаруживать ошибки на ранней стадии. Строгая типизированная кодовая база повышает качество и масштабируемость кода, способствуя более эффективному и безопасному сотрудничеству в команде.
Однако иногда определение типов может потребовать много повторяющейся работы. Сопоставленные типы — очень полезная функция для уменьшения повторяемости и сохранения нашего кода СУХИМ. Это также помогает иметь меньше источников истины для наших определений типов.