Вопрос или проблема
У меня есть набор историй для компонента в Storybook 8 и Angular 18, которые показывают ошибку типов, когда компонент включает EventEmitter. Например:
class MyComponent {
@Output() someEvent = new EventEmitter<any>();
// другие свойства и методы
}
И простая история, например:
export default meta;
type Story = StoryObj<MyComponent>;
export const Basic: Story = {
render: (args: MyComponent) => ({
props: {
...args,
},
}),
args: {},
};
Как в моей среде разработки (VS Code), так и при запуске в браузере я получаю ошибку типов и не могу запустить свои истории. Ошибка, кажется, несущественна и просто указывает на несоответствие типов (для справки, store – это служба в моем компоненте):
Тип ‘(args: MyComponent) => { props: { someEvent: EventEmitter<…>;
}; }’ недопустим для типа ‘ArgsStoryFn<AngularRenderer,
TransformInputSignalType<TransformOutputSignalType<TransformEventType>>>’.
Типы параметров ‘args’ и ‘args’ несовместимы.
Тип ‘TransformInputSignalType<TransformOutputSignalType<TransformEventType>>’
не имеет следующих свойств типа ‘MyComponent’: store
Если я удаляю любые @Output EventEmitters, проблема исчезает, но, как вы можете представить, мне нужны эти эмиттеры для работы моего компонента.
Я наткнулся на обходной путь, изменив тип StoryObj на ‘any’ вот так:
export default meta;
type Story = StoryObj<any>;
export const Basic: Story = {
render: (args: MyComponent) => ({
props: {
...args,
},
}),
args: {},
};
Однако это не кажется правильным, и я подозреваю, что я просто делаю что-то не так.
Ответ или решение
Ошибка типа функции рендеринга в Storybook v8 и Angular v18: подробное объяснение и решение
При разработке компонентов на основе Angular и использовании Storybook для их документирования, вы можете столкнуться с рядом типовых ошибок. Одной из таких ошибок является несовпадение типов, возникающее, когда ваш компонент содержит EventEmitter
. Давайте разберем вашу ситуацию более подробно и предложим решения.
Описание проблемы
В вашем компоненте используется EventEmitter
, который объявляется следующим образом:
class MyComponent {
@Output() someEvent = new EventEmitter<any>();
// другие свойства и методы
}
При создании простого Story для этого компонента, вы получаете ошибку типов, касающуюся параметров функции render
:
export default meta;
type Story = StoryObj<MyComponent>;
export const Basic: Story = {
render: (args: MyComponent) => ({
props: {
...args,
},
}),
args: {},
};
Ошибка, с которой вы сталкиваетесь, гласит, что тип аргумента args
несовместим с ожидаемым типом. Ваша IDE и браузер выдают сообщение о том, что TransformInputSignalType
не содержит свойство store
, которое определено в вашем компоненте:
Type '(args: MyComponent) => {...}' is not assignable to type 'ArgsStoryFn<AngularRenderer, ...>'.
Types of parameters 'args' and 'args' are incompatible. Type 'TransformInputSignalType' is missing the following properties from type 'MyComponent': store
Анализ проблемы
Основная причина возникновения данного типа ошибки заключается в том, что Storybook и Angular используют собственные системы типов, которые интерпретируют параметры по-разному, особенно в отношении выходных и входных свойств компонентов. Output
свойства, такие как EventEmitter
, добавляют определенные особенности к вашему компоненту, которые могут привести к несовпадениям.
Когда вы убираете @Output
и EventEmitter
, Storybook не сталкивается с проблемами сопоставления типов, так как становится проще определить структуру данных, передаваемых в render
.
Решение через изменение типа StoryObj
Ваше временное решение, заключающееся в установлении типа StoryObj<any>
, может устранить ошибку, но, как вы правильно заметили, это не является идеальной практикой. Использование any
может скрывать реальные проблемы и привести к недостатку типизации.
Рекомендации по решению
- Определите входные свойства явно: Убедитесь, что вы определили все свойства вашего компонента в типе для StoryObj. Если ваш компонент нуждается в дополнительных свойствах, добавьте их в интерфейс Story:
interface MyComponentArgs {
store: MyStoreService; // Замените на реальный тип вашего сервиса
// Добавьте другие необходимые свойства
}
- Обновите StoryObj: Вместо использования
any
, установите правильный тип:
type Story = StoryObj<MyComponentArgs>;
export const Basic: Story = {
render: (args: MyComponentArgs) => ({
props: {
...args,
},
}),
args: {
store: new MyStoreService(), // Или другой способ предоставить ваш сервис
},
};
-
Проверка конфигурации Storybook: Убедитесь, что ваши зависимости Storybook и Angular обновлены до последних версий. Порой, такие проблемы могут быть исправлены в более новых релизах.
-
Документация и примеры: Обратитесь к документации Storybook и Angular для более подробной информации о том, как правильно интегрировать эти технологии.
Заключение
Правильная типизация и конфигурация Storybook с Angular необходимы для обеспечения гладкого процесса разработки. Понимание специфик типов, используемых в обеих средах, может помочь избежать потенциальных проблем. Если вы продолжаете сталкиваться с трудностями, не стесняйтесь обращаться за помощью в сообщества разработчиков, такие как Stack Overflow или специализированные форумы.
Эффективная работа с типами и событиями — это залог успешного и безопасного кода в любой значимой разработке на Angular.