Что такое тип записи в машинописном тексте?
Что означает Record<K, T>
в Typescript?
Typescript 2.1 представил тип Record
, описав его в примере:
// For every properties K of type T, transform it to U
function mapObject<K extends string, T, U>(obj: Record<K, T>, f: (x: T) => U): Record<K, U>
см. Typescript 2.1
А на странице " Расширенные типы" упоминается " Record
под заголовком " Readonly
типы" наряду с " Readonly
, " Partial
и " Pick
, что, как представляется, является ее определением:
type Record<K extends string, T> = {
[P in K]: T;
}
Только для чтения, Partial и Pick гомоморфны, а Record - нет. Один признак того, что Record не является гомоморфным, заключается в том, что для копирования свойств не требуется тип ввода:
type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>
И это оно. Помимо приведенных цитат, нет других упоминаний о Record
на typescriptlang.org.
Вопросы
-
Может кто-нибудь дать простое определение того, что такое Record
?
-
Является ли Record<K,T>
просто способом сказать "все свойства этого объекта будут иметь тип T
"? Вероятно, не все свойства, так как K
имеет какое-то назначение...
-
Запрещает ли общий тип K
дополнительные ключи на объекте, которые не являются K
, или он разрешает их и просто указывает, что их свойства не преобразуются в T
?
-
С приведенным примером:
type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>
Это точно так же, как это?
type ThreeStringProps = {prop1: string, prop2: string, prop3: string}
Ответы
Ответ 1
- Может ли кто-нибудь дать простое определение того, что такое
Record
?
A Record<K, T>
- это тип объекта, ключом свойства которого являются K
и значениями свойств которого являются T
То есть, keyof Record<K, T>
эквивалентна K
, а Record<K, T>[K]
(в основном) эквивалентна T
- Является ли
Record<K,T>
просто способом сказать, что "все свойства этого объекта будут иметь тип T
"? Вероятно, не все объекты, так как K
имеет определенную цель...
Как вы заметили, K
имеет цель... ограничить ключи свойств конкретными значениями. Если вы хотите принять все возможные строковые ключи, вы можете сделать что-то вроде Record<string, T>
, но идиоматический способ сделать это - использовать подпись индекса, такую как { [k: string]: T }
.
- Предоставляет ли
K
generic дополнительные ключи на объекте, который не является K
, или он позволяет им, и просто указывает, что их свойства не преобразуются в T
?
Он не совсем "запрещает" дополнительные ключи: в конце концов, как правило, для значений обычно допускаются свойства, явно не упомянутые в его типе... но он не признает, что такие свойства существуют:
declare const x: Record<"a", string>;
x.b; // error, Property 'b' does not exist on type 'Record<"a", string>'
и он будет относиться к ним как к избыточным свойствам, которые иногда отвергаются:
declare function acceptR(x: Record<"a", string>): void;
acceptR({a: "hey", b: "you"}); // error, Object literal may only specify known properties
и иногда принимается:
const y = {a: "hey", b: "you"};
acceptR(y); // okay
-
В данном примере:
type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>
Это точно так же, как это?:
type ThreeStringProps = {prop1: string, prop2: string, prop3: string}
Да!
Надеюсь, это поможет. Удачи!
Ответ 2
Запись позволяет вам создать новый тип из Союза. Значения в Союзе используются в качестве атрибутов нового типа.
Например, скажем, у меня есть Союз, подобный этому:
type CatNames = "miffy" | "boris" | "mordred";
Теперь я хочу создать объект, который содержит информацию обо всех кошках, я могу создать новый тип, используя значения в Союзе CatName в качестве ключей.
type CatList = Record<CatNames, {age: number}>
Если я хочу удовлетворить этот CatList, я должен создать такой объект:
const cats:CatList = {
miffy: { age:99 },
boris: { age:16 },
mordred: { age:600 }
}
Вы получаете очень сильный тип безопасности:
- Если я забуду кота, я получу ошибку.
- Если я добавлю кошку, которая не разрешена, я получу ошибку.
- Если я позже поменяю CatNames, я получу ошибку. Это особенно полезно, потому что CatNames, вероятно, импортируется из другого файла и, вероятно, используется во многих местах.
Реальный пример React.
Я использовал это недавно, чтобы создать компонент Status. Компонент получит реквизит состояния, а затем отобразит значок. Я очень упростил код здесь для наглядности
У меня был такой союз:
type Statuses = "failed" | "complete";
Я использовал это, чтобы создать объект, подобный этому:
const icons: Record<
Statuses,
{ iconType: IconTypes; iconColor: IconColors }
> = {
failed: {
iconType: "warning",
iconColor: "red"
},
complete: {
iconType: "check",
iconColor: "green"
};
Затем я могу сделать рендеринг, разложив элемент из объекта в подпорки, например, так:
const Status = ({status}) => <Icon {...icons[status]} />
Если объединение "Статусы" будет позже расширено или изменено, я знаю, что мой компонент "Статус" не удастся скомпилировать, и я получу ошибку, которую можно исправить немедленно. Это позволяет мне добавлять дополнительные состояния ошибок в приложение.
Обратите внимание, что в настоящем приложении были десятки состояний ошибок, на которые ссылались в нескольких местах, поэтому безопасность этого типа была чрезвычайно полезной.