Repository: pytransitions/transitions (language: python)

Issue:
Not possible to use list of Transition objects
Thank you for taking the time to report a bug! Your support is essential for the maintenance of this project. Please fill out the following fields to ease bug hunting and resolving this issue as soon as possible:

**Describe the bug**
Annotation of the lib says that I can use `Transition` object for `transitions` parameter:
```
class Machine:
    ...
    def __init__(self, model: Optional[ModelParameter] = ...,
        ...
        transitions: Optional[Union[TransitionConfig, Sequence[TransitionConfig]]] = ...,
        ...
    )

...
TransitionConfig = Union[Sequence[Union[str, Any]], Dict[str, Any], Transition]
```
However if I try to do that...:
`TypeError: transitions.core.Machine.add_transition() argument after ** must be a mapping, not Transition`

**Minimal working example**
```python
import transitions

transitions.Machine(
    states=["1", "2"],
    transitions=[
        transitions.Transition(source="1", dest="2"),
    ],
)
```

**Expected behavior**
should be one of:
1. it works
2. annotation does not say that this is possible

**Additional context**
Add any other context about the problem here.

Not possible to use list of Transition objects
Thank you for taking the time to report a bug! Your support is essential for the maintenance of this project. Please fill out the following fields to ease bug hunting and resolving this issue as soon as possible:

**Describe the bug**
Annotation of the lib says that I can use `Transition` object for `transitions` parameter:
```
class Machine:
    ...
    def __init__(self, model: Optional[ModelParameter] = ...,
        ...
        transitions: Optional[Union[TransitionConfig, Sequence[TransitionConfig]]] = ...,
        ...
    )

...
TransitionConfig = Union[Sequence[Union[str, Any]], Dict[str, Any], Transition]
```
However if I try to do that...:
`TypeError: transitions.core.Machine.add_transition() argument after ** must be a mapping, not Transition`

**Minimal working example**
```python
import transitions

transitions.Machine(
    states=["1", "2"],
    transitions=[
        transitions.Transition(source="1", dest="2"),
    ],
)
```

**Expected behavior**
should be one of:
1. it works
2. annotation does not say that this is possible

**Additional context**
Add any other context about the problem here.



Discussion / hints:

Fix patch:
diff --git a/Changelog.md b/Changelog.md
index 36331186..7b3c48cd 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,5 +1,12 @@
 # Changelog
 
+## 0.9.3 ()
+
+- Bug #683: Typing wrongly suggested that `Transition` instances can be passed to `Machine.__init__` and/or `Machine.add_transition(s)` (thanks @antonio-antuan)
+- Typing should be more precise now
+  - `Machine.add_transitions` and `Machine.__init__` expect a `Sequence` of configurations for transitions now
+  - Added 'async' callbacks to types in `asyncio` extension
+
 ## 0.9.2 (August 2024)
 
 Release 0.9.2 is a minor release and contains a new mermaid diagram backend, a new model decoration mode for easier development with types and some more features and bugfixes.
diff --git a/README.md b/README.md
index 7c642afe..547454a6 100644
--- a/README.md
+++ b/README.md
@@ -1,11 +1,11 @@
 # <a name="transitions-module"></a> transitions
 
-[![Version](https://img.shields.io/badge/version-v0.9.2-orange.svg)](https://github.com/pytransitions/transitions)
+[![Version](https://img.shields.io/badge/version-v0.9.3-orange.svg)](https://github.com/pytransitions/transitions)
 [![Build Status](https://github.com/pytransitions/transitions/actions/workflows/pytest.yml/badge.svg)](https://github.com/pytransitions/transitions/actions?query=workflow%3Apytest)
 [![Coverage Status](https://coveralls.io/repos/github/pytransitions/transitions/badge.svg?branch=master)](https://coveralls.io/github/pytransitions/transitions?branch=master)
 [![PyPi](https://img.shields.io/pypi/v/transitions.svg)](https://pypi.org/project/transitions)
 [![Copr](https://img.shields.io/badge/dynamic/json?color=blue&label=copr&query=builds.latest.source_package.version&url=https%3A%2F%2Fcopr.fedorainfracloud.org%2Fapi_3%2Fpackage%3Fownername%3Daleneum%26projectname%3Dpython-transitions%26packagename%3Dpython-transitions%26with_latest_build%3DTrue)](https://copr.fedorainfracloud.org/coprs/aleneum/python-transitions/)
-[![GitHub commits](https://img.shields.io/github/commits-since/pytransitions/transitions/0.9.1.svg)](https://github.com/pytransitions/transitions/compare/0.9.1...master)
+[![GitHub commits](https://img.shields.io/github/commits-since/pytransitions/transitions/0.9.2.svg)](https://github.com/pytransitions/transitions/compare/0.9.2...master)
 [![License](https://img.shields.io/github/license/pytransitions/transitions.svg)](LICENSE)
 [![Binder](https://mybinder.org/badge_logo.svg)](https://mybinder.org/v2/gh/pytransitions/transitions/master?filepath=examples%2FPlayground.ipynb)
 
diff --git a/transitions/core.pyi b/transitions/core.pyi
index 55728067..c5204d2d 100644
--- a/transitions/core.pyi
+++ b/transitions/core.pyi
@@ -1,7 +1,7 @@
 from logging import Logger
 from typing import (
     Any, Optional, Callable, Sequence, Union, Iterable, List, Dict, DefaultDict,
-    Type, Deque, OrderedDict, Tuple, Literal, Collection, TypedDict
+    Type, Deque, OrderedDict, Tuple, Literal, Collection, TypedDict, Mapping
 )
 
 # Enums are supported for Python 3.4+ and Python 2.7 with enum34 package installed
@@ -87,7 +87,12 @@ class Transition:
     def add_callback(self, trigger: str, func: Callback) -> None: ...
     def __repr__(self) -> str: ...
 
-TransitionConfig = Union[Sequence[Union[str, Any]], Dict[str, Any], Transition]
+TransitionConfigList = Union[
+    List[str], List[Sequence[str]], List[Optional[str]],
+    List[Union[str, Enum]], List[Optional[Union[str, Enum]]]
+]
+TransitionConfigDict = Mapping[str, Union[None, StateConfig, Callback, Iterable[Callback]]]
+TransitionConfig = Union[TransitionConfigList, TransitionConfigDict]
 
 class EventData:
     state: State
@@ -145,7 +150,7 @@ class Machine:
     def __init__(self, model: Optional[ModelParameter] = ...,
                  states: Optional[Union[Sequence[StateConfig], Type[Enum]]] = ...,
                  initial: Optional[StateIdentifier] = ...,
-                 transitions: Optional[Union[TransitionConfig, Sequence[TransitionConfig]]] = ...,
+                 transitions: Optional[Sequence[TransitionConfig]] = ...,
                  send_event: bool = ..., auto_transitions: bool = ..., ordered_transitions: bool = ...,
                  ignore_invalid_triggers: Optional[bool] = ...,
                  before_state_change: CallbacksArg = ..., after_state_change: CallbacksArg = ...,
@@ -211,7 +216,7 @@ class Machine:
                        conditions: CallbacksArg = ..., unless: CallbacksArg = ...,
                        before: CallbacksArg = ..., after: CallbacksArg = ..., prepare: CallbacksArg = ...,
                        **kwargs: Dict[str, Any]) -> None: ...
-    def add_transitions(self, transitions: Union[TransitionConfig, List[TransitionConfig]]) -> None: ...
+    def add_transitions(self, transitions: Sequence[TransitionConfig]) -> None: ...
     def add_ordered_transitions(self, states: Optional[Sequence[Union[str, State]]] = ...,
                                 trigger: str = ..., loop: bool = ...,
                                 loop_includes_initial: bool = ...,
diff --git a/transitions/experimental/utils.pyi b/transitions/experimental/utils.pyi
index 95cf531a..4258a19f 100644
--- a/transitions/experimental/utils.pyi
+++ b/transitions/experimental/utils.pyi
@@ -1,4 +1,5 @@
-from typing import Union, Callable, List, Optional, Iterable, Type, ClassVar, Tuple, Dict, Any, DefaultDict, Deque
+from typing import Union, Callable, List, Optional, Iterable, Type, ClassVar, Tuple, Dict, Any, DefaultDict, Deque, \
+    Sequence
 from transitions.core import StateIdentifier, CallbacksArg, CallbackFunc, Machine, TransitionConfig, MachineConfig
 from transitions.extensions.markup import MarkupConfig
 
@@ -8,7 +9,7 @@ def generate_base_model(config: Union[MachineConfig, MarkupConfig]) -> str: ...
 
 def with_model_definitions(cls: Type[Machine]) -> Type[Machine]: ...
 
-def add_transitions(*configs: TransitionConfig) -> Callable[[CallbackFunc], CallbackFunc]: ...
+def add_transitions(*configs: Union[TransitionConfig, Sequence[TransitionConfig]]) -> Callable[[CallbackFunc], CallbackFunc]: ...
 def event(*configs: TransitionConfig) -> Callable[..., Optional[bool]]: ...
 
 def transition(source: Union[StateIdentifier, List[StateIdentifier]],
diff --git a/transitions/extensions/asyncio.pyi b/transitions/extensions/asyncio.pyi
index 080e4751..0d00df1a 100644
--- a/transitions/extensions/asyncio.pyi
+++ b/transitions/extensions/asyncio.pyi
@@ -1,22 +1,28 @@
-from ..core import Callback, Condition, Event, EventData, Machine, State, Transition, StateConfig, ModelParameter, TransitionConfig
-from .nesting import HierarchicalMachine, NestedEvent, NestedState, NestedTransition, NestedEventData
-from typing import Any, Awaitable, Optional, List, Type, Dict, Deque, Callable, Union, Iterable, DefaultDict, Literal, Sequence
+from ..core import Callback, Condition, Event, EventData, Machine, State, Transition, StateConfig, ModelParameter, \
+    TransitionConfigList
+from .nesting import HierarchicalMachine, NestedEvent, NestedState, NestedTransition, NestedEventData, \
+    NestedStateConfig, NestedStateIdentifier
+from typing import Any, Awaitable, Optional, List, Type, Dict, Deque, Callable, Union, Iterable, DefaultDict, Literal, \
+    Sequence, Coroutine, Mapping
 from asyncio import Task
-from functools import partial
 from logging import Logger
 from enum import Enum
 from contextvars import ContextVar
 
-from ..core import StateIdentifier, CallbacksArg, CallbackList
+from ..core import StateIdentifier, CallbackList
 
 _LOGGER: Logger
 
-AsyncCallbackFunc = Callable[..., Awaitable[Optional[bool]]]
+AsyncCallbackFunc = Callable[..., Coroutine[Any, Any, Optional[bool]]]
 AsyncCallback = Union[str, AsyncCallbackFunc]
+AsyncCallbacksArg = Optional[Union[Callback, Iterable[Callback], AsyncCallback, Iterable[AsyncCallback]]]
+AsyncTransitionConfigDict = Mapping[str, Union[None, StateConfig, Union[AsyncCallback, Callback], Iterable[Union[AsyncCallback, Callback]]]]
+AsyncTransitionConfig = Union[TransitionConfigList, AsyncTransitionConfigDict]
 
 class AsyncState(State):
     async def enter(self, event_data: AsyncEventData) -> None: ...  # type: ignore[override]
     async def exit(self, event_data: AsyncEventData) -> None: ...  # type: ignore[override]
+    def add_callback(self, trigger: str, func: AsyncCallback) -> Awaitable[Optional[bool]]: ...  # type: ignore[override]
 
 class NestedAsyncState(NestedState, AsyncState):
     _scope: Any
@@ -73,16 +79,23 @@ class AsyncMachine(Machine):
     def __init__(self, model: Optional[ModelParameter] = ...,
                  states: Optional[Union[Sequence[StateConfig], Type[Enum]]] = ...,
                  initial: Optional[StateIdentifier] = ...,
-                 transitions: Optional[Union[TransitionConfig, Sequence[TransitionConfig]]] = ...,
+                 transitions: Optional[Sequence[AsyncTransitionConfig]] = ...,
                  send_event: bool = ..., auto_transitions: bool = ..., ordered_transitions: bool = ...,
                  ignore_invalid_triggers: Optional[bool] = ...,
-                 before_state_change: CallbacksArg = ..., after_state_change: CallbacksArg = ...,
+                 before_state_change: AsyncCallbacksArg = ..., after_state_change: AsyncCallbacksArg = ...,
                  name: str = ..., queued: Union[bool, Literal["model"]] = ...,
-                 prepare_event: CallbacksArg = ..., finalize_event: CallbacksArg = ...,
-                 model_attribute: str = ..., model_override: bool= ..., on_exception: CallbacksArg = ...,
-                 on_final: CallbacksArg = ..., **kwargs: Dict[str, Any]) -> None: ...
+                 prepare_event: AsyncCallbacksArg = ..., finalize_event: AsyncCallbacksArg = ...,
+                 model_attribute: str = ..., model_override: bool= ..., on_exception: AsyncCallbacksArg = ...,
+                 on_final: AsyncCallbacksArg = ..., **kwargs: Dict[str, Any]) -> None: ...
     def add_model(self, model: Union[Union[Literal["self"], object], Sequence[Union[Literal["self"], object]]],
                   initial: Optional[StateIdentifier] = ...) -> None: ...
+    def add_transition(self, trigger: str,
+                       source: Union[StateIdentifier, List[StateIdentifier]],
+                       dest: Optional[StateIdentifier] = ...,
+                       conditions: AsyncCallbacksArg = ..., unless: AsyncCallbacksArg = ...,
+                       before: AsyncCallbacksArg = ..., after: AsyncCallbacksArg = ..., prepare: AsyncCallbacksArg = ...,
+                       **kwargs: Dict[str, Any]) -> None: ...
+    def add_transitions(self, transitions: Sequence[AsyncTransitionConfig] = ...) -> None: ...
     async def dispatch(self, trigger: str, *args: List[Any], **kwargs: Dict[str, Any]) -> bool: ...  # type: ignore[override]
     async def callbacks(self, funcs: Iterable[Callback], event_data: AsyncEventData) -> None: ...  # type: ignore[override]
     async def callback(self, func: AsyncCallback, event_data: AsyncEventData) -> None: ...  # type: ignore[override]
@@ -99,7 +112,17 @@ class HierarchicalAsyncMachine(HierarchicalMachine, AsyncMachine):  # type: igno
     state_cls: Type[NestedAsyncState]
     transition_cls: Type[NestedAsyncTransition]
     event_cls: Type[NestedAsyncEvent]  # type: ignore
-    async def trigger_event(self, model: object, trigger: str, # type: ignore[override]
+    def __init__(self, model: Optional[ModelParameter]=...,
+                 states: Optional[Union[Sequence[NestedStateConfig], Type[Enum]]] = ...,
+                 initial: Optional[NestedStateIdentifier] = ...,
+                 transitions: Sequence[AsyncTransitionConfig] = ...,
+                 send_event: bool = ..., auto_transitions: bool = ..., ordered_transitions: bool = ...,
+                 ignore_invalid_triggers: Optional[bool] = ...,
+                 before_state_change: AsyncCallbacksArg = ..., after_state_change: AsyncCallbacksArg = ...,
+                 name: str = ..., queued: Union[bool, str] = ...,
+                 prepare_event: AsyncCallbacksArg = ..., finalize_event: AsyncCallbacksArg = ...,
+                 model_attribute: str = ..., on_exception: AsyncCallbacksArg = ..., **kwargs: Dict[str, Any]) -> None: ...
+    async def trigger_event(self, model: object, trigger: str,  # type: ignore[override]
                             *args: List[Any], **kwargs: Dict[str, Any]) -> bool: ...
     async def _trigger_event(self, event_data: NestedAsyncEventData, trigger: str) -> bool: ...  # type: ignore[override]
 
@@ -109,7 +132,7 @@ class HierarchicalAsyncMachine(HierarchicalMachine, AsyncMachine):  # type: igno
 class AsyncTimeout(AsyncState):
     dynamic_methods: List[str]
     timeout: float
-    _on_timeout: CallbacksArg
+    _on_timeout: AsyncCallbacksArg
     runner: Dict[int, Task[Any]]
     def __init__(self, *args: List[Any], **kwargs: Dict[str, Any]) -> None: ...
     async def enter(self, event_data: AsyncEventData) -> None: ...  # type: ignore[override]
@@ -119,7 +142,7 @@ class AsyncTimeout(AsyncState):
     @property
     def on_timeout(self) -> CallbackList: ...
     @on_timeout.setter
-    def on_timeout(self, value: CallbacksArg) -> None: ...
+    def on_timeout(self, value: AsyncCallbacksArg) -> None: ...
 
 class _DictionaryMock(Dict[Any, Any]):
     _value: Any
diff --git a/transitions/extensions/diagrams.pyi b/transitions/extensions/diagrams.pyi
index 567cd59e..a8c2686f 100644
--- a/transitions/extensions/diagrams.pyi
+++ b/transitions/extensions/diagrams.pyi
@@ -37,8 +37,8 @@ class GraphMachine(MarkupMachine):
     def __init__(self, model: Optional[ModelParameter]=...,
                  states: Optional[Union[Sequence[StateConfig], Type[Enum]]] = ...,
                  initial: Optional[StateIdentifier] = ...,
-                 transitions: Optional[Union[TransitionConfig, List[TransitionConfig]]] = ..., send_event: bool = ...,
-                 auto_transitions: bool = ..., ordered_transitions: bool = ...,
+                 transitions: Optional[Sequence[TransitionConfig]] = ...,
+                 send_event: bool = ..., auto_transitions: bool = ..., ordered_transitions: bool = ...,
                  ignore_invalid_triggers: Optional[bool] = ...,
                  before_state_change: CallbacksArg = ..., after_state_change: CallbacksArg = ...,
                  name: str = ..., queued: bool = ...,
diff --git a/transitions/extensions/factory.pyi b/transitions/extensions/factory.pyi
index f329b4fc..21b01e91 100644
--- a/transitions/extensions/factory.pyi
+++ b/transitions/extensions/factory.pyi
@@ -47,7 +47,7 @@ class LockedHierarchicalGraphMachine(GraphMachine, LockedHierarchicalMachine):
     @staticmethod
     def format_references(func: CallbackFunc) -> str: ...
 
-class AsyncGraphMachine(GraphMachine, AsyncMachine):
+class AsyncGraphMachine(GraphMachine, AsyncMachine):  # type: ignore
     # AsyncTransition already considers graph models when necessary
     transition_cls: Type[AsyncTransition]  # type: ignore
 
diff --git a/transitions/version.py b/transitions/version.py
index 0d302254..0bcbac4a 100644
--- a/transitions/version.py
+++ b/transitions/version.py
@@ -2,4 +2,4 @@
     to determine transitions' version during runtime.
 """
 
-__version__ = '0.9.2'
+__version__ = '0.9.3'


Test patch:
diff --git a/tests/test_async.py b/tests/test_async.py
index b3200064..fadbca0d 100644
--- a/tests/test_async.py
+++ b/tests/test_async.py
@@ -1,12 +1,12 @@
-from transitions.extensions.asyncio import AsyncMachine, HierarchicalAsyncMachine
 from transitions.extensions.factory import AsyncGraphMachine, HierarchicalAsyncGraphMachine
 
 try:
     import asyncio
+    from transitions.extensions.asyncio import AsyncMachine, HierarchicalAsyncMachine, AsyncEventData
+
 except (ImportError, SyntaxError):
     asyncio = None  # type: ignore
 
-
 from unittest.mock import MagicMock
 from unittest import skipIf
 from functools import partial
@@ -17,7 +17,8 @@
 from .test_pygraphviz import pgv
 
 if TYPE_CHECKING:
-    from typing import Type
+    from typing import Type, Sequence
+    from transitions.extensions.asyncio import AsyncTransitionConfig
 
 
 @skipIf(asyncio is None, "AsyncMachine requires asyncio and contextvars suppport")
@@ -218,6 +219,7 @@ def test_queued(self):
         # Define with list of dictionaries
 
         async def change_state(machine):
+            # type: (AsyncMachine) -> None
             self.assertEqual(machine.state, 'A')
             if machine.has_queue:
                 await machine.run(machine=machine)
@@ -227,12 +229,14 @@ async def change_state(machine):
                     await machine.run(machine=machine)
 
         async def raise_machine_error(event_data):
+            # type: (AsyncEventData) -> None
             self.assertTrue(event_data.machine.has_queue)
             await event_data.model.to_A()
             event_data.machine._queued = False
             await event_data.model.to_C()
 
         async def raise_exception(event_data):
+            # type: (AsyncEventData) -> None
             await event_data.model.to_C()
             raise ValueError("Clears queue")
 
@@ -240,7 +244,7 @@ async def raise_exception(event_data):
             {'trigger': 'walk', 'source': 'A', 'dest': 'B', 'before': change_state},
             {'trigger': 'run', 'source': 'B', 'dest': 'C'},
             {'trigger': 'sprint', 'source': 'C', 'dest': 'D'}
-        ]
+        ]  # type: Sequence[AsyncTransitionConfig]
 
         m = self.machine_cls(states=states, transitions=transitions, initial='A')
         asyncio.run(m.walk(machine=m))
@@ -269,10 +273,12 @@ def check_mock():
         m2 = DummyModel()
 
         async def run():
-            transitions = [{'trigger': 'mock', 'source': ['A', 'B'], 'dest': 'B', 'after': mock},
-                           {'trigger': 'delayed', 'source': 'A', 'dest': 'B', 'before': partial(asyncio.sleep, 0.1)},
-                           {'trigger': 'check', 'source': 'B', 'dest': 'A', 'after': check_mock},
-                           {'trigger': 'error', 'source': 'B', 'dest': 'C', 'before': self.raise_value_error}]
+            transitions = [
+                {'trigger': 'mock', 'source': ['A', 'B'], 'dest': 'B', 'after': mock},
+                {'trigger': 'delayed', 'source': 'A', 'dest': 'B', 'before': partial(asyncio.sleep, 0.1)},
+                {'trigger': 'check', 'source': 'B', 'dest': 'A', 'after': check_mock},
+                {'trigger': 'error', 'source': 'B', 'dest': 'C', 'before': self.raise_value_error}
+            ]  # type: Sequence[AsyncTransitionConfig]
             m = self.machine_cls(model=[m1, m2], states=['A', 'B', 'C'], transitions=transitions, initial='A',
                                  queued='model')
             # call m1.delayed and m2.mock should be called immediately
@@ -307,7 +313,7 @@ def check_queue(expect, event_data):
              'before': partial(check_queue, 4), 'after': remove_model},
             {'trigger': 'remove_queue', 'source': 'B', 'dest': None, 'prepare': ['to_A', 'to_C'],
              'before': partial(check_queue, 3), 'after': remove_model}
-        ]
+        ]  # type: Sequence[AsyncTransitionConfig]
 
         async def run():
             m1 = DummyModel()
@@ -599,13 +605,15 @@ def test_nested_async(self):
         mock = MagicMock()
 
         async def sleep_mock():
+            # type: () -> None
             await asyncio.sleep(0.1)
             mock()
 
         states = ['A', 'B', {'name': 'C', 'children': ['1', {'name': '2', 'children': ['a', 'b'], 'initial': 'a'},
                                                        '3'], 'initial': '2'}]
-        transitions = [{'trigger': 'go', 'source': 'A', 'dest': 'C',
-                        'after': [sleep_mock] * 100}]
+        transitions = [
+            {'trigger': 'go', 'source': 'A', 'dest': 'C', 'after': [sleep_mock] * 100}
+        ]  # type: Sequence[AsyncTransitionConfig]
         machine = self.machine_cls(states=states, transitions=transitions, initial='A')
         asyncio.run(machine.go())
         self.assertEqual('C{0}2{0}a'.format(machine.state_cls.separator), machine.state)
diff --git a/tests/test_core.py b/tests/test_core.py
index db59ff39..0debdbb6 100644
--- a/tests/test_core.py
+++ b/tests/test_core.py
@@ -22,7 +22,8 @@
     from mock import MagicMock  # type: ignore
 
 if TYPE_CHECKING:
-    from typing import List, Union, Dict, Callable
+    from typing import Sequence
+    from transitions.core import TransitionConfig, StateConfig
 
 
 def on_exit_A(event):
@@ -104,7 +105,7 @@ def test_transition_definitions(self):
             {'trigger': 'walk', 'source': 'A', 'dest': 'B'},
             {'trigger': 'run', 'source': 'B', 'dest': 'C'},
             {'trigger': 'sprint', 'source': 'C', 'dest': 'D'}
-        ]  # type: List[Union[List[str], Dict[str, str]]]
+        ]  # type: Sequence[TransitionConfig]
         m = Machine(states=states, transitions=transitions, initial='A')
         m.walk()
         self.assertEqual(m.state, 'B')
@@ -351,7 +352,7 @@ def test_send_event_data_conditions(self):
         self.assertEqual(s.state, 'B')
 
     def test_auto_transitions(self):
-        states = ['A', {'name': 'B'}, State(name='C')]  # type: List[Union[str, Dict[str, str], State]]
+        states = ['A', {'name': 'B'}, State(name='C')]  # type: Sequence[StateConfig]
         m = Machine(states=states, initial='A', auto_transitions=True)
         m.to_B()
         self.assertEqual(m.state, 'B')
@@ -609,7 +610,7 @@ def change_state(machine):
             {'trigger': 'walk', 'source': 'A', 'dest': 'B', 'before': change_state},
             {'trigger': 'run', 'source': 'B', 'dest': 'C'},
             {'trigger': 'sprint', 'source': 'C', 'dest': 'D'}
-        ]
+        ]  # type: Sequence[TransitionConfig]
 
         m = Machine(states=states, transitions=transitions, initial='A')
         m.walk(machine=m)
@@ -628,8 +629,11 @@ def after_change(machine):
             machine.to_C(machine)
 
         states = ['A', 'B', 'C']
-        transitions = [{'trigger': 'do', 'source': '*', 'dest': 'C',
-                        'before': partial(self.stuff.this_raises, ValueError)}]
+        transitions = [{
+            'trigger': 'do', 'source': '*', 'dest': 'C',
+            'before': partial(self.stuff.this_raises, ValueError)
+        }]  # type: Sequence[TransitionConfig]
+
         m = Machine(states=states, transitions=transitions, queued=True,
                     before_state_change=before_change, after_state_change=after_change)
         with self.assertRaises(MachineError):
@@ -972,7 +976,7 @@ def always_fails():
             {'trigger': 'go', 'source': 'A', 'dest': 'B', 'conditions': always_fails, 'prepare': local_callback},
             {'trigger': 'go', 'source': 'A', 'dest': 'B', 'prepare': local_callback},
 
-        ]
+        ]  # type: Sequence[TransitionConfig]
         m = Machine(states=['A', 'B'], transitions=transitions,
                     prepare_event=global_callback, initial='A')
 
diff --git a/tests/test_enum.py b/tests/test_enum.py
index b1257851..cde9532e 100644
--- a/tests/test_enum.py
+++ b/tests/test_enum.py
@@ -1,3 +1,4 @@
+from enum import Enum
 from unittest import TestCase, skipIf
 
 from transitions.core import Machine
@@ -15,7 +16,8 @@
 from .test_graphviz import pgv as gv
 
 if TYPE_CHECKING:
-    from typing import Type, List, Union, Dict
+    from typing import Type, List, Union, Dict, Sequence
+    from transitions.core import TransitionConfig
 
 
 @skipIf(enum is None, "enum is not available")
@@ -73,7 +75,7 @@ def test_property_initial(self):
         transitions = [
             {'trigger': 'switch_to_yellow', 'source': self.States.RED, 'dest': self.States.YELLOW},
             {'trigger': 'switch_to_green', 'source': 'YELLOW', 'dest': 'GREEN'},
-        ]
+        ]  # type: Sequence[TransitionConfig]
 
         m = self.machine_cls(states=self.States, initial=self.States.RED, transitions=transitions)
         m.switch_to_yellow()
@@ -137,7 +139,7 @@ class State(IntEnum):
         transitions = [
             ['foo', State.FOO, State.BAR],
             ['bar', State.BAR, State.FOO]
-        ]
+        ]  # type: Sequence[List[Union[str, Enum]]]
 
         m = self.machine_cls(states=State, initial=State.FOO, transitions=transitions)
         m.foo()
@@ -418,4 +420,5 @@ class Invalid(enum.Enum):
             INVALID = 1
 
         with self.assertRaises(ValueError):
-            self.machine_cls(states=States, transitions=[['go', '*', Invalid.INVALID]], initial=States.ONE)
+            transitions = [['go', '*', Invalid.INVALID]]  # type: Sequence[List[Union[str, Enum]]]
+            self.machine_cls(states=States, transitions=transitions, initial=States.ONE)
diff --git a/tests/test_experimental.py b/tests/test_experimental.py
index 755681c8..804344ba 100644
--- a/tests/test_experimental.py
+++ b/tests/test_experimental.py
@@ -1,4 +1,4 @@
-from typing import TYPE_CHECKING
+from typing import TYPE_CHECKING, Sequence, Union, List
 from unittest import TestCase
 from types import ModuleType
 from unittest.mock import MagicMock
@@ -11,7 +11,7 @@
 from .utils import Stuff
 
 if TYPE_CHECKING:
-    from transitions.core import MachineConfig
+    from transitions.core import MachineConfig, TransitionConfig
     from typing import Type
 
 
@@ -137,8 +137,10 @@ class Model:
             def is_B(self) -> bool:
                 return False
 
+            transition_config = [["A", "B"], "C"]  # type: TransitionConfig
+
             @add_transitions(transition(source="A", dest="B"))
-            @add_transitions([["A", "B"], "C"])
+            @add_transitions(transition_config)
             def go(self) -> bool:
                 raise RuntimeError("Should be overridden!")
 
@@ -194,7 +196,8 @@ class Model:
             def is_B(self) -> bool:
                 return False
 
-            go = event(transition(source="A", dest="B"), [["A", "B"], "C"])
+            transition_config = [["A", "B"], "C"]  # type: TransitionConfig
+            go = event(transition(source="A", dest="B"), transition_config)
 
         model = Model()
         machine = self.trigger_machine(model, states=["A", "B", "C"], initial="A")
diff --git a/tests/test_graphviz.py b/tests/test_graphviz.py
index 4a59070f..c3ee1e21 100644
--- a/tests/test_graphviz.py
+++ b/tests/test_graphviz.py
@@ -19,7 +19,8 @@
     pgv = None
 
 if TYPE_CHECKING:
-    from typing import Type, List, Collection, Union, Literal
+    from typing import Type, List, Collection, Union, Literal, Sequence, Dict, Optional
+    from transitions.core import TransitionConfig
 
 
 class TestDiagramsImport(TestCase):
@@ -74,7 +75,7 @@ def setUp(self):
             {'trigger': 'run', 'source': 'B', 'dest': 'C'},
             {'trigger': 'sprint', 'source': 'C', 'dest': 'D', 'conditions': 'is_fast'},
             {'trigger': 'sprint', 'source': 'C', 'dest': 'B'}
-        ]
+        ]  # type: Sequence[Dict[str, str]]
 
     def test_diagram(self):
         m = self.machine_cls(states=self.states, transitions=self.transitions, initial='A', auto_transitions=False,
@@ -325,7 +326,8 @@ def setUp(self):
             {'trigger': 'sprint', 'source': 'C', 'dest': 'D',    # + 1 edge
              'conditions': 'is_fast'},
             {'trigger': 'sprint', 'source': 'C', 'dest': 'B'},   # + 1 edge
-            {'trigger': 'reset', 'source': '*', 'dest': 'A'}]    # + 4 edges (from base state) = 8
+            {'trigger': 'reset', 'source': '*', 'dest': 'A'}     # + 4 edges (from base state) = 8
+        ]  # type: Sequence[Dict[str, str]]
 
     def test_diagram(self):
         m = self.machine_cls(states=self.states, transitions=self.transitions, initial='A', auto_transitions=False,
@@ -419,9 +421,11 @@ def test_roi_parallel_deeper(self):
 
     def test_internal(self):
         states = ['A', 'B']
-        transitions = [['go', 'A', 'B'],
-                       dict(trigger='fail', source='A', dest=None, conditions=['failed']),
-                       dict(trigger='fail', source='A', dest='B', unless=['failed'])]
+        transitions = [
+            ['go', 'A', 'B'],
+            dict(trigger='fail', source='A', dest=None, conditions=['failed']),
+            dict(trigger='fail', source='A', dest='B', unless=['failed'])
+        ]  # type: Sequence[TransitionConfig]
         m = self.machine_cls(states=states, transitions=transitions, initial='A', show_conditions=True,
                              graph_engine=self.graph_engine)
 
@@ -442,7 +446,7 @@ def test_internal_wildcards(self):
             {"trigger": "polled", "source": "ready", "dest": "running", "conditions": "door_closed"},
             ["done", "running", "ready"],
             ["polled", "*", None]
-        ]
+        ]  # type: Sequence[TransitionConfig]
         m = self.machine_cls(states=states, transitions=transitions, show_conditions=True,
                              graph_engine=self.graph_engine, initial='initial')
         _, nodes, edges = self.parse_dot(m.get_graph())
diff --git a/tests/test_markup.py b/tests/test_markup.py
index 42a168cd..ea04de07 100644
--- a/tests/test_markup.py
+++ b/tests/test_markup.py
@@ -28,7 +28,8 @@ class Enum:  # type: ignore
         pass
 
 if TYPE_CHECKING:
-    from typing import List, Dict, Type, Union
+    from typing import List, Dict, Sequence, Union, Type
+    from transitions.core import TransitionConfig, StateConfig
 
 
 class SimpleModel(object):
@@ -96,12 +97,12 @@ class TestMarkupMachine(TestCase):
 
     def setUp(self):
         self.machine_cls = MarkupMachine
-        self.states = ['A', 'B', 'C', 'D']  # type: Union[List[Union[str, Dict]], Type[Enum]]
+        self.states = ['A', 'B', 'C', 'D']  # type: Union[Sequence[StateConfig], Type[Enum]]
         self.transitions = [
             {'trigger': 'walk', 'source': 'A', 'dest': 'B'},
             {'trigger': 'run', 'source': 'B', 'dest': 'C'},
             {'trigger': 'sprint', 'source': 'C', 'dest': 'D'}
-        ]  # type: List[Union[str, Dict[str, Union[str, Enum]]]]
+        ]  # type: Sequence[TransitionConfig]
         self.num_trans = len(self.transitions)
         self.num_auto = len(self.states) ** 2
 
diff --git a/tests/test_nesting.py b/tests/test_nesting.py
index 8e975fe2..f709e82f 100644
--- a/tests/test_nesting.py
+++ b/tests/test_nesting.py
@@ -1,5 +1,4 @@
 # -*- coding: utf-8 -*-
-
 try:
     from builtins import object
 except ImportError:
@@ -31,8 +30,8 @@
 
 
 if TYPE_CHECKING:
-    from typing import List, Dict, Union, Type
-
+    from typing import List, Dict, Union, Type, Sequence
+    from transitions.core import TransitionConfig
 
 default_separator = NestedState.separator
 
@@ -661,7 +660,7 @@ def test_get_nested_triggers(self):
             ['goA', '*', 'A'],
             ['goF1', ['C{0}1'.format(self.machine_cls.separator), 'C{0}2'.format(self.machine_cls.separator)], 'F'],
             ['goF2', 'C', 'F']
-        ]
+        ]  # type: Sequence[TransitionConfig]
         m = self.machine_cls(states=test_states, transitions=transitions, auto_transitions=False, initial='A')
         self.assertEqual(1, len(m.get_nested_triggers(['C', '1'])))
         with m('C'):
diff --git a/tests/test_reuse.py b/tests/test_reuse.py
index 3d47eb45..6387cd86 100644
--- a/tests/test_reuse.py
+++ b/tests/test_reuse.py
@@ -19,7 +19,8 @@
 
 
 if TYPE_CHECKING:
-    from typing import List, Union, Dict, Any
+    from typing import List, Union, Dict, Any, Sequence
+    from transitions.core import TransitionConfig
 
 
 test_states = ['A', 'B', {'name': 'C', 'children': ['1', '2', {'name': '3', 'children': ['a', 'b', 'c']}]},
@@ -199,7 +200,7 @@ def test_example_reuse(self):
             ['decrease', '3', '2'],
             ['decrease', '2', '1'],
             {'trigger': 'done', 'source': '3', 'dest': 'done', 'conditions': 'this_passes'},
-        ]
+        ]  # type: Sequence[TransitionConfig]
 
         counter = self.machine_cls(states=count_states, transitions=count_trans, initial='1')
         counter.increase()  # love my counter
@@ -211,7 +212,7 @@ def test_example_reuse(self):
             ['collect', '*', 'collecting'],
             ['wait', '*', 'waiting'],
             ['count', '*', 'counting%s1' % State.separator]
-        ]
+        ]  # type: Sequence[TransitionConfig]
 
         collector = self.stuff.machine_cls(states=states, transitions=transitions, initial='waiting')
         collector.this_passes = self.stuff.this_passes
@@ -264,7 +265,7 @@ def test_reuse_add_state(self):
             ['decrease', '3', '2'],
             ['decrease', '2', '1'],
             {'trigger': 'done', 'source': '3', 'dest': 'done', 'conditions': 'this_passes'},
-        ]
+        ]  # type: Sequence[TransitionConfig]
 
         counter = self.machine_cls(states=count_states, transitions=count_trans, initial='1')
         counter.increase()  # love my counter
@@ -322,7 +323,7 @@ def test_reuse_model_decoration(self):
             ['decrease', '3', '2'],
             ['decrease', '2', '1'],
             {'trigger': 'done', 'source': '3', 'dest': 'done', 'conditions': 'this_passes'},
-        ]
+        ]  # type: Sequence[TransitionConfig]
 
         counter = self.machine_cls(states=count_states, transitions=count_trans, initial='1')
         states_remap = ['waiting', 'collecting'] \
@@ -354,7 +355,7 @@ def test_reuse_model_decoration_add_state(self):
             ['decrease', '3', '2'],
             ['decrease', '2', '1'],
             {'trigger': 'done', 'source': '3', 'dest': 'done', 'conditions': 'this_passes'},
-        ]
+        ]  # type: Sequence[TransitionConfig]
 
         counter = self.machine_cls(states=count_states, transitions=count_trans, initial='1')
         states_remap = ['waiting', 'collecting'] \
@@ -529,7 +530,7 @@ def check_self(self):
         transitions = [
             {"trigger": "go", "source": "A", "dest": "B",
              "conditions": m.check_self, "prepare": m.check_self, "before": m.check_self, "after": m.check_self}
-        ]
+        ]  # type: Sequence[TransitionConfig]
 
         child = self.machine_cls(None, states=["A", "B"], transitions=transitions, initial="A")
         parent = self.machine_cls(m, states=[{"name": "P", "states": child, "remap": {}}], initial="P")
diff --git a/tests/test_states.py b/tests/test_states.py
index 92318791..0bca4856 100644
--- a/tests/test_states.py
+++ b/tests/test_states.py
@@ -14,7 +14,8 @@
 
 
 if TYPE_CHECKING:
-    from typing import Type
+    from typing import Type, Sequence
+    from transitions.core import TransitionConfig
 
 
 class TestTransitions(TestCase):
@@ -58,7 +59,7 @@ class CustomMachine(self.machine_cls):
                   {'name': 'S2', 'accepted': True}]
 
         transitions = [['to_B', ['S1', 'S2'], 'B'], ['go', 'A', 'B'], ['fail', 'B', 'F'],
-                       ['success1', 'B', 'S2'], ['success2', 'B', 'S2']]
+                       ['success1', 'B', 'S2'], ['success2', 'B', 'S2']]  # type: Sequence[TransitionConfig]
         m = CustomMachine(states=states, transitions=transitions, auto_transitions=False, initial='A')
         m.go()
         m.success1()