Вопрос: есть ли способ написать аннотацию типа в TypeScript, которая допускает любой литерал объекта, но не позволяет использовать встроенные типы number
, string
, boolean
, Function
или Array
?
Почему?
Я работал над улучшением некоторых определений типов в DefinitelyTyped для некоторых библиотек, которые я использую в своих проектах на работе. Обычный шаблон, который я заметил во многих библиотеках JS, заключается в передаче объекта «параметров», используемого для настройки библиотеки или плагина. В этих случаях вы часто будете видеть определения типов, выглядящие примерно так:
declare module 'myModule' {
interface IMyModule {
(opts?: IOptions): NodeJS.ReadWriteStream;
}
interface IOptions {
callback?: Function;
readonly?: boolean;
}
}
Это позволяет использовать его следующим образом:
var myModule = require('myModule');
myModule();
myModule({});
myModule({ callback: () => {} });
myModule({ readonly: false });
myModule({ callback: () => {}, readonly: false });
Все они являются допустимыми вариантами использования. Проблема в том, что эти определения типов также допускают следующие недопустимые варианты использования:
myModule('hello');
myModule(false);
myModule(42);
myModule(() => {});
myModule([]);
Во многих случаях вышеуказанные вызовы приведут к ошибке времени выполнения, поскольку JS-код библиотеки может попытаться установить для объекта значения по умолчанию или передать параметры другой библиотеке. Хотя я пробовал много вещей, мне не удалось ограничить параметр, чтобы он принимал только объекты, а не какие-либо другие недопустимые случаи.
Кажется, что если у вас есть интерфейс только с необязательными членами (поскольку ни одна из опций не требуется), компилятор расширит допустимые типы, чтобы принять any
.
Вы можете увидеть демонстрацию этой проблемы на TypeScript Playground здесь: http://bit.ly/1Js7tLr
Обновление: пример решения, которое не работает
interface IOptions {
[name: string]: any;
callback?: Function;
readonly?: boolean;
}
Эта попытка требует присутствия оператора индексации в объекте, что означает, что теперь он жалуется на number
s, string
s, boolean
s, Function
s и Array
s. Но это создает новую проблему:
var opts = {};
myModule(opts);
Теперь это терпит неудачу, когда это должно быть допустимым сценарием. (См. это в Playground: http://bit.ly/1MPbxfX)