При работе с массивами справедливо следующее:
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
, что мне делать?
... так это проблема XY? Вы хотите «получить все свойства типа ключа string
»? Может быть, отредактируйте вопрос так, чтобы это был заголовок, и тогда в вашем вопросе можно было бы кратко упомянуть ваши неудачные попытки. Ответ здесь либо A[keyof A]
, либо A[string & keyof A]
, в зависимости от того, насколько отчаянно вам нужно игнорировать ключи, отличные от string
. Это полностью решает вопрос? Или я что-то упускаю?
@jcalz: О, я думал, что string
достаточно, и не осознавал, что мне следует использовать строковые литералы для каждого из ключей. Тот A[string & keyof A]
решает мой обновленный вопрос. Если каждый тип строкового литерала является подтипом string
, то почему я не могу просто использовать string
в качестве индекса здесь?
Если вы пишете T[K]
, то K
должен быть подтипом keyof T
, а не наоборот. Если у вас есть объект a
типа A
и ключ k
типа string
, писать a[k]
в TypeScript — ошибка, потому что этот ключ вполне может не появиться в объекте. Так что писать A[string]
тоже нельзя. TS жалуется, потому что string
не является ключом A
. И если бы он не жаловался, A[string]
, вероятно, был бы unknown
, потому что существуют всевозможные возможные ключи, которые может иметь значение типа A
.
Если вы хотите индексировать тип объекта 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"
Учитывая контекст вопроса, вы, вероятно, неправильно понимаете еще одну вещь: typeof MyArray[number]
на самом деле не имеет ничего общего с ключами типа number
. Индексация с number
означает только то, что не имеет значения, какой элемент массива индексируется, поскольку все они одинаковы. Итак, вместо typeof MyArray[0]
или typeof MyArray[1]
вы можете просто использовать number
.
Я думаю, что это не то, о чем спрашивают (хотя KeysOfA
— вводящее в заблуждение имя в вопросе ОП). На самом деле он просто ищет индексированные доступы (ключи типа string
, а не значения типа string
).
Хм... Как правильно получить все свойства определенного типа ключа... для меня звучало очень однозначно. Теперь я тоже уже не уверен.
Слово «ключ» здесь имеет значение. Свойства определенного типа ключа. Из моих запросов на разъяснения в комментариях становится достаточно ясно, что ОП на самом деле просто пытается выполнить индексированный доступ.
Разве тип ключа не является типом ключа (=PropertyKey
)? В любом случае, почитав комментарии, становится ясно, что вы правы (как всегда ;D). Я сохраню свой ответ не удалённым, если ОП не будет отредактирован соответствующим образом. Возможно, это будет полезно будущим читателям.
Да, проблема в моем названии. Я должен назвать это PropertiesOfKeyType
или что-то в этом роде. Если вы не возражаете, я обновлю свой вопрос. Извиняюсь за это.
Я не понимаю вторую часть вашего ответа. Если string
недействителен для A[string]
, потому что он включает (много) несуществующих ключей, таких как 'b'
, то почему number
не вызовет проблемы для MyArray[number]
? Например, MyArray[4]
— это неверный доступ.
number
работает только при индексировании типа массива. Тип массива TypeScript (по сравнению с кортежем) не имеет фиксированной длины. typeof MyArray
это просто { name: string; age: number; }[]
. Массив переменных времени выполнения содержит 3 элемента, но на уровне типа информации об этом нет. Теоретически массив может иметь любую длину. Поэтому индексировать с помощью number
зачастую точнее, чем использовать случайные индексы, которые индексируют один и тот же тип. К вашему сведению, typeof MyArray[4]
(а также 3
или 23802302
) действительны. Попробуйте.
?? Потому что
A
— это тип, а не значение. Вы спрашиваете, «почему индексированные типы доступа» не работают, но они работают нормально. Что не «работает», так этоtypeof
. Не пишитеtypeof A
, просто пишитеA
. ТипA[string]
. Но это не так, потому чтоstring
не является ключом кA
, поэтому вы ищетеA['a']
или что-то в этом роде? Что за... ой, смотри, ты редактируешь вопрос, чтобы задать несколько вещей одновременно. Пожалуйста, отредактируйте так, чтобы это был один вопрос об одной проблеме.