#typescript
Вопрос:
Если у меня есть перечисление и я хочу использовать значения перечисления в качестве ключа в некоторых объектах, я могу объявить типы следующим образом:
enum Foo {
X, Y
}
let map: FooMapType = {
[Foo.X]: "abc",
[Foo.Y]: "def"
}
type FooMapType = {
[key in Foo]: string
}
Это работает совершенно нормально. Однако, если я попытаюсь сделать то же самое с помощью интерфейса:
interface FooMapInterface {
[key in Foo]: string
}
Я получаю эти ошибки:
TS1169: A computed property name in an interface must refer to an expression whose type is a literal type or a 'unique symbol' type.
TS2464: A computed property name must be of type 'string', 'number', 'symbol', or 'any'.
TS2304: Cannot find name 'key'.
Почему в этом случае существует разница между интерфейсом и типом? Каковы ограничения на интерфейс, если таковые имеются, которые привели к этому дизайнерскому решению?
Ответ №1:
Вам не разрешается использовать сопоставленные ключи типа (формы [P in K]: T
, где K
тип похож на ключ) в качестве ключей an interface
. Прямо сейчас вы получаете несколько сбивающих с толку сообщений об ошибках, потому что компилятор неверно истолковывает то, что вы делаете, как использование объявления вычисляемого свойства (формы [k]: U
, в которой k
используется значение, подобное ключу, а не тип). Существует открытая проблема в microsoft/TypeScript#18299 с просьбой исправить эти сообщения об ошибках.
Я не знаю, есть ли какая-либо каноническая документация, в которой точно говорится, почему вы не можете использовать сопоставленные типы здесь. В дополнение к обычным свойствам, ключи которых являются статически известными строковыми или числовыми литеральными типами, интерфейсам разрешается иметь подписи вызовов, подписи конструкций, подписи методов и подписи индексов, все из которых также разрешены внутри class
типов экземпляров. Сопоставленные типы не входят в список допустимых вещей.
Если вы хотите использовать interface
сопоставленный тип, вы можете сделать это, расширив сопоставленный тип в качестве нового интерфейса. Вам разрешено расширять именованный тип с помощью статически известных ключей. И поскольку ключи FooMapType
статически известны (в отличие от некоторого неопределенного универсального типа), вы можете написать это:
interface FooMapInterface extends FooMapType { } // okay
И проследите, чтобы это сработало:
declare const fooMapInterface: FooMapInterface;
fooMapInterface[Foo.X].toUpperCase(); // okay
Таким образом, вы можете получить такое поведение, вы просто не можете написать его напрямую.