Как правильно получить все свойства определенного типа ключа из интерфейса в TypeScript?

При работе с массивами справедливо следующее:

const MyArray = [
  { name: "Alice", age: 15 },
  { name: "Bob", age: 23 },
  { name: "Eve", age: 38 },
];
 
type Person = typeof MyArray[number];

Затем я попробовал ту же идею с интерфейсами:

interface A {
  a: number;
}

type PropertiesTypeByString = A[string]

но у меня возникла ошибка:

Type 'A' has no matching index signature for type 'string'.

Учитывая, что я хочу получить все свойства типа ключа string, что мне делать?

?? Потому что A — это тип, а не значение. Вы спрашиваете, «почему индексированные типы доступа» не работают, но они работают нормально. Что не «работает», так это typeof. Не пишите typeof A, просто пишите A. Тип A[string]. Но это не так, потому что string не является ключом к A, поэтому вы ищете A['a'] или что-то в этом роде? Что за... ой, смотри, ты редактируешь вопрос, чтобы задать несколько вещей одновременно. Пожалуйста, отредактируйте так, чтобы это был один вопрос об одной проблеме.

jcalz 18.04.2024 14:33

... так это проблема XY? Вы хотите «получить все свойства типа ключа string»? Может быть, отредактируйте вопрос так, чтобы это был заголовок, и тогда в вашем вопросе можно было бы кратко упомянуть ваши неудачные попытки. Ответ здесь либо A[keyof A], либо A[string & keyof A], в зависимости от того, насколько отчаянно вам нужно игнорировать ключи, отличные от string. Это полностью решает вопрос? Или я что-то упускаю?

jcalz 18.04.2024 14:37

@jcalz: О, я думал, что string достаточно, и не осознавал, что мне следует использовать строковые литералы для каждого из ключей. Тот A[string & keyof A] решает мой обновленный вопрос. Если каждый тип строкового литерала является подтипом string, то почему я не могу просто использовать string в качестве индекса здесь?

NeoZoom.lua 18.04.2024 14:45

Если вы пишете T[K], то K должен быть подтипом keyof T, а не наоборот. Если у вас есть объект a типа A и ключ k типа string, писать a[k] в TypeScript — ошибка, потому что этот ключ вполне может не появиться в объекте. Так что писать A[string] тоже нельзя. TS жалуется, потому что string не является ключом A. И если бы он не жаловался, A[string], вероятно, был бы unknown, потому что существуют всевозможные возможные ключи, которые может иметь значение типа A.

jcalz 18.04.2024 14:51
Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой 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 для повышения производительности приложения путем загрузки модулей только тогда, когда они...
0
4
52
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

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

Если вы хотите индексировать тип объекта A, ключ должен быть назначен keyof A (с помощью оператора типа keyof ). Поскольку A не имеет индексной подписи string , вы не можете писать A[string]. Вместо этого вы можете пересечь keyof A с string, чтобы получить «все ключи A типа string:

const sym = Symbol();
interface A {
  a: number;
  b: boolean;
  3: Date; // numeric key, not string
  [sym]: Element // symbol key, not string
}

type PropertiesTypeByString  = A[keyof A & string];
//   ^? type PropertiesTypeByString  = number | boolean

Ссылка на код детской площадки

Вы можете отфильтровать ключи, которые не соответствуют определенному типу (например, string), с помощью Сопоставленного типа и Сопоставления клавиш. Если ключ K расширяется string, он сохраняется в объекте, в противном случае он удаляется путем переназначения его на never.

interface A {
  a: number;
  1: string;
}

type KeysOfType<T, U extends PropertyKey> = keyof {
  [K in keyof T as K extends U ? K : never]: T[K];
};
type Result = KeysOfType<A, string>;
//   ^? type Result = "a"

Игровая площадка TypeScript


Учитывая контекст вопроса, вы, вероятно, неправильно понимаете еще одну вещь: typeof MyArray[number] на самом деле не имеет ничего общего с ключами типа number. Индексация с number означает только то, что не имеет значения, какой элемент массива индексируется, поскольку все они одинаковы. Итак, вместо typeof MyArray[0] или typeof MyArray[1] вы можете просто использовать number.

Я думаю, что это не то, о чем спрашивают (хотя KeysOfA — вводящее в заблуждение имя в вопросе ОП). На самом деле он просто ищет индексированные доступы (ключи типа string, а не значения типа string).

jcalz 18.04.2024 14:58

Хм... Как правильно получить все свойства определенного типа ключа... для меня звучало очень однозначно. Теперь я тоже уже не уверен.

Behemoth 18.04.2024 15:02

Слово «ключ» здесь имеет значение. Свойства определенного типа ключа. Из моих запросов на разъяснения в комментариях становится достаточно ясно, что ОП на самом деле просто пытается выполнить индексированный доступ.

jcalz 18.04.2024 15:03

Разве тип ключа не является типом ключа (=PropertyKey)? В любом случае, почитав комментарии, становится ясно, что вы правы (как всегда ;D). Я сохраню свой ответ не удалённым, если ОП не будет отредактирован соответствующим образом. Возможно, это будет полезно будущим читателям.

Behemoth 18.04.2024 15:11

Да, проблема в моем названии. Я должен назвать это PropertiesOfKeyType или что-то в этом роде. Если вы не возражаете, я обновлю свой вопрос. Извиняюсь за это.

NeoZoom.lua 18.04.2024 15:13

Я не понимаю вторую часть вашего ответа. Если string недействителен для A[string], потому что он включает (много) несуществующих ключей, таких как 'b', то почему number не вызовет проблемы для MyArray[number]? Например, MyArray[4] — это неверный доступ.

NeoZoom.lua 18.04.2024 15:32
number работает только при индексировании типа массива. Тип массива TypeScript (по сравнению с кортежем) не имеет фиксированной длины. typeof MyArray это просто { name: string; age: number; }[]. Массив переменных времени выполнения содержит 3 элемента, но на уровне типа информации об этом нет. Теоретически массив может иметь любую длину. Поэтому индексировать с помощью number зачастую точнее, чем использовать случайные индексы, которые индексируют один и тот же тип. К вашему сведению, typeof MyArray[4] (а также 3 или 23802302) действительны. Попробуйте.
Behemoth 18.04.2024 15:56

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