# Extend type checking

!!! abstract ""

    How to use `register_type_namespace` to make forward references from downstream packages resolvable at composition time, enabling third-party types in app pipelines.

## The problem

When you compose apps with `+`, `scinexus` checks that the output type of the left app is compatible with the input type of the right app. Type hints are often written as forward references — strings like `"Alignment"` or `"PhyloNode"` — to avoid circular imports. At composition time `scinexus` must resolve these strings to actual classes, but it only knows about its own types by default. If your package defines custom types used in app hints, `scinexus` cannot resolve them without help.

## The solution

`register_type_namespace` lets a downstream package register a **lazy namespace provider** — a zero-argument callable that returns a `dict[str, type]`. When `scinexus` encounters an unresolved forward reference, it queries each registered provider in order until it finds a match.

```python { notest }
from scinexus.typing import register_type_namespace

register_type_namespace(my_provider)
```

The provider is called lazily each time a name needs resolving, so the package can defer heavy imports. Providers are responsible for their own caching. Registration is idempotent: re-registering the same callable is a no-op.

<!-- [[[cog
from cog_utils import exec_codeblock

src = \
"""
from scinexus.typing import register_type_namespace

_resolution_ns = None

def _get_resolution_namespace():
    global _resolution_ns
    if _resolution_ns is not None:
        return _resolution_ns

    from cogent3.core.alignment import Alignment, SequenceCollection  # (1)!
    from cogent3.core.tree import PhyloNode
    # ... other imports ...

    _resolution_ns = {
        "Alignment": Alignment,
        "SequenceCollection": SequenceCollection,
        "PhyloNode": PhyloNode,  # (3)!
        # ... other types ...
    }
    return _resolution_ns

register_type_namespace(_get_resolution_namespace)  # (2)!
"""
exec_codeblock(src=src, admonition='???- example "How `cogent3` does it"',
annotations=["`cogent3` defines many types (`Alignment`, `PhyloNode`, `Table`, etc.) that are used as forward references in app type hints.",
"In `cogent3/app/typing.py`, a resolution namespace is built lazily and registered with `scinexus`",
'With this registration, any `scinexus` app that uses `"PhyloNode"` as a type hint will resolve correctly at composition time without the user importing `PhyloNode` explicitly.'
])
  ]]] -->
???- example "How `cogent3` does it"

    ```python { linenums="1" notest }
    from scinexus.typing import register_type_namespace

    _resolution_ns = None

    def _get_resolution_namespace():
        global _resolution_ns
        if _resolution_ns is not None:
            return _resolution_ns

        from cogent3.core.alignment import Alignment, SequenceCollection  # (1)!
        from cogent3.core.tree import PhyloNode
        # ... other imports ...

        _resolution_ns = {
            "Alignment": Alignment,
            "SequenceCollection": SequenceCollection,
            "PhyloNode": PhyloNode,  # (3)!
            # ... other types ...
        }
        return _resolution_ns

    register_type_namespace(_get_resolution_namespace)  # (2)!
    ```

    1. `cogent3` defines many types (`Alignment`, `PhyloNode`, `Table`, etc.) that are used as forward references in app type hints.
    2. In `cogent3/app/typing.py`, a resolution namespace is built lazily and registered with `scinexus`
    3. With this registration, any `scinexus` app that uses `"PhyloNode"` as a type hint will resolve correctly at composition time without the user importing `PhyloNode` explicitly.
<!-- [[[end]]] -->


## Registering your own package's types

Follow the same pattern: define a lazy provider function that imports and caches your types, then register it at module level.

```python { notest }
from scinexus.typing import register_type_namespace

_ns = None


def _get_my_types():
    global _ns
    if _ns is not None:
        return _ns

    from my_package.core import MyDataType, MyResultType

    _ns = {
        "MyDataType": MyDataType,
        "MyResultType": MyResultType,
    }
    return _ns


register_type_namespace(_get_my_types)
```

Place this in a module that is imported early (e.g. your package's `typing.py` or `__init__.py`). Once registered, apps using `"MyDataType"` as a forward reference will resolve correctly when composed with other apps.
