Ответ 1
Такое поведение уходит.
Начиная с любого выпуска, следующего после TypeScript 1.8 (или прямо сейчас, если вы используете компилятор с кратким выпуском), вы больше не увидите эту ошибку, когда исходное выражение для типа является литералом объекта.
См. https://github.com/Microsoft/TypeScript/pull/7029
Старый ответ для старых компиляторов
Индексы и объектные литералы ведут себя особенно в TypeScript. Из спецификации 4.5, Object Literals:
Когда объектный литерал контекстуально вводится типом, который включает строковая индексная подпись типа T, результирующий тип объекта literal включает строчную индексную подпись с расширенной формой наилучший общий тип T и типы свойств, объявленных в объектный литерал.
Что это значит?
Контекстный ввод
Контекстная типизация возникает, когда контекст выражения дает подсказку о том, каким может быть его тип. Например, в этой инициализации:
var x: number = y;
Выражение y
получает контекстный тип number
, потому что он инициализирует значение этого типа. В этом случае ничего особенного не происходит, но в других случаях будет происходить более интересное.
Одним из наиболее полезных случаев являются функции:
// Error: string does not contain a function called 'ToUpper'
var x: (n: string) => void = (s) => console.log(s.ToUpper());
Как компилятор знал, что s
является строкой? Если вы сами написали это выражение функции, s
будет иметь тип any
, и не было бы никакой ошибки. Но поскольку функция была введена в контекстном типе типа x
, параметр s
приобрел тип string
. Очень полезно!
Индексные подписи
Индексная подпись указывает тип, когда объект индексируется строкой или числом. Естественно, эти подписи являются частью проверки типов:
var x: { [n: string]: Car; };
var y: { [n: string]: Animal; };
x = y; // Error: Cars are not Animals, this is invalid
Важное значение имеет также отсутствие сигнатуры индекса:
var x: { [n: string]: Car; };
var y: { name: Car; };
x = y; // Error: y doesn't have an index signature that returns a Car
Надеюсь, очевидно, что эти два фрагмента должны вызывать ошибки. Что приводит нас к...
Индексные подписи и контекстный ввод
Проблема с допущением, что объекты не имеют индексных сигнатур, заключается в том, что у вас нет способа инициализировать объект с помощью сигнатуры индекса:
var c: Car;
// Error, or not?
var x: { [n: string]: Car } = { 'mine': c };
Решение состоит в том, что когда литерал объекта контекстуально вводится типом с сигнатурой индекса, эта подпись индекса добавляется к типу объектного литерала, если он соответствует. Например:
var c: Car;
var a: Animal;
// OK
var x: { [n: string]: Car } = { 'mine': c };
// Not OK: Animal is not Car
var y: { [n: string]: Car } = { 'mine': a };
Вводя все вместе
Посмотрим на исходные функции в вопросе:
function a(): StringMap {
return { a: "1" }; // OK
}
ОК, потому что выражения в операторах return
конкретизируются типом возвращаемой функции. Литерал объекта {a: "1"}
имеет строковое значение для его единственного свойства, поэтому подпись индекса может быть успешно применена.
function b(): StringMap {
var result: StringMap = { a: "1" };
return result; // OK
}
ОК, потому что инициализаторы явно типизированных переменных контекстуально печатаются по типу переменной. Как и раньше, подпись индекса добавляется к типу объектного литерала.
function c(): StringMap {
var result = { a: "1" };
return result; // Error - result lacks index signature, why?
}
Не в порядке, потому что тип result
не имеет индексной подписи.