Почему добавление круглых скобок удаляет «только для чтения [x: число]: число;» из этого типа?

enum Animals {
  CAT,
  DOG,
  PARROT,
  SHEEP,
  SALMON
}

Следующий тип:

type AnimalsToMappedType = {
  [K in keyof typeof Animals]: K
}

Результаты:

type AnimalsToMappedType = {
  readonly [x: number]: number;
  readonly CAT: "CAT";
  readonly DOG: "DOG";
  readonly PARROT: "PARROT";
  readonly SHEEP: "SHEEP";
  readonly SALMON: "SALMON";
}

Но если вы добавите круглые скобки вокруг keyof typeof Animals:

type AnimalsToMappedType = {
  [K in (keyof typeof Animals)]: K
}

Результат:

type AnimalsToMappedType = {
    CAT: "CAT";
    DOG: "DOG";
    PARROT: "PARROT";
    SHEEP: "SHEEP";
    SALMON: "SALMON";
}

ВОПРОС

Что делают скобки, которые удаляют readonly [x: number]: number;?

Пожалуйста, отредактируйте, чтобы четко задать один четко сформулированный вопрос. Сейчас это выглядит как несколько вопросов, большая часть которых выглядит как проблема XY, поскольку у вас есть некоторый базовый вариант использования (написание сопоставленного типа), и ваши вопросы касаются неудачных попыток. Если вы спрашиваете об основном варианте использования, я бы сказал, напишите { [K in string & keyof typeof Animals as typeof Animals[K]]: K }. Если вы спрашиваете о поведении ваших попыток, задайте только один вопрос об одной такой попытке, а остальные разбейте на собственные вопросы, если они останутся после получения ответа на этот вопрос.

jcalz 19.06.2024 16:52

@jcalz это было намного яснее, прежде чем кто-то решил взять на себя задачу отредактировать это

james 19.06.2024 17:31

Хм, я все еще вижу там несколько вопросов. Какой у вас главный вопрос?

jcalz 19.06.2024 17:33

Спасибо. Без скобок это гомоморфный отображаемый тип, где TS распознает, что вы выполняете итерацию по «всем свойствам typeof Animals»; в каком-то смысле in keyof — это отдельная синтаксическая единица. В случае круглых скобок он не является гомоморфным и просто перебирает все ключи, содержащиеся в выражении. Числовые объекты-перечисления имеют обратные сопоставления в своем типе (и сигнатуру числового индекса), но number не включен в keyof typeof Animals. Отсюда и разница. Это полностью решает вопрос? Если да, то я напишу полную а; если нет, то чего не хватает?

jcalz 19.06.2024 17:58

(см. предыдущий комментарий...github.com/microsoft/TypeScript/issues/40206 является канонической проблемой для этого, кстати, я включу это в свой ответ, если вы согласны, что он полностью решает вопрос)

jcalz 19.06.2024 18:00
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
2
5
65
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

См. microsoft/TypeScript#40206 для получения авторитетного ответа.

Сопоставленный тип формы {[K in keyof XXX]: YYY} считается гомоморфным отображаемым типом , где TypeScript видит in keyof и распознает, что вы преобразуете какой-то другой тип XXX, и поэтому может использовать информацию об исходном типе, которого нет. в результатах keyof. С другой стороны, отображаемый тип подобного вида {[K in (keyof XXX)]: YYY} не считается гомоморфным. Круглые скобки заставляют TypeScript сначала вычислять keyof XXX, а затем отображаемые типы перебирают эти результаты, ничего не зная и не помня о XXX. Итак, in keyof XXX хранит информацию о XXX вокруг того, что in (keyof XXX) выбрасывает.

Эта разница чаще всего наблюдается при преобразовании типа со свойствами необязательных и только для чтения. Гомоморфный отображаемый тип сохранит необязательный /readonly, даже если такая информация не видна через keyof. Например, если XXX равно {a?: string, readonly b: number}, то {[K in keyof XXX]: YYY} будет производить {a?: YYY, readonly b: YYY}, тогда как {[K in (keyof XXX): YYY} будет производить {a: YYY, b: YYY}.

В вашем примере вы работаете с объектом числового перечисления , который имеет обратные сопоставления . Если Animals.CAT === 0, то Animals[0] === "CAT". TypeScript моделирует это, добавляя числовую подпись индекса к объекту перечисления, но эта подпись индекса подавляется при использовании keyof. Таким образом, хотя typeof Animals может выглядеть как { [k: number]: string; CAT: 0 }, keyof typeof Animals — это просто "CAT", а не number | "CAT". Таким образом, гомоморфный отображаемый тип для числового перечисления будет иметь числовую сигнатуру индекса, тогда как негомоморфный отображаемый тип для числового перечисления не будет.

Другие вопросы по теме