Можно ли создать два интерфейса или типа с одинаковым именем, но разным описанием в TypeScript
Что проверяют
Знаете ли вы про слияние деклараций и его границы. В Node это не теория: так расширяют Express.Request, NodeJS.ProcessEnv и globalThis, и на собеседовании проверяют, делали ли вы это руками.
Как отвечать
Для интерфейсов ответ да: два одноимённых interface в одной области видимости сливаются в один, это declaration merging. Для псевдонимов типа ответ нет: второй такой же даёт ошибку Duplicate identifier.
Важные оговорки:
- слияние работает только внутри одной области. В модуле (файл с
importилиexport) интерфейс локален, и чтобы дополнить чужой тип, нужен блокdeclare moduleилиdeclare global; - поле с одинаковым именем и разными типами это ошибка компиляции, а не «побеждает последнее объявление»;
- для методов вместо ошибки получаются перегрузки, и объявления из более позднего блока встают в списке раньше;
- сливаются не только интерфейсы: так же ведут себя одноимённые пространства имён, а пространство имён умеет дополнять функцию, класс и перечисление.
declare global {
namespace Express {
interface Request { userId?: string }
}
}
export {};Где спотыкаются
Кладут declare global в файл без единого импорта и экспорта. Такой файл считается скриптом, а не модулем, и объявление либо запрещено, либо утекает в глобальную область целиком. Лечится строкой export {};.
Уверенно повторяют, что при конфликте полей компилятор берёт последнее объявление. Это неверно, он выдаёт ошибку о несовпадении типов, и вопрос рассчитан ровно на это.
Пишут файл описаний и забывают, что он должен попасть в компиляцию: файл не лежит под include или в tsconfig.json сужен список types. Типы молча не применяются, а разработчик ищет причину внутри самого расширения.