diff --git a/python/src/typechat/_internal/ts_conversion/python_type_to_ts_nodes.py b/python/src/typechat/_internal/ts_conversion/python_type_to_ts_nodes.py index 8616c423..e87da3c3 100644 --- a/python/src/typechat/_internal/ts_conversion/python_type_to_ts_nodes.py +++ b/python/src/typechat/_internal/ts_conversion/python_type_to_ts_nodes.py @@ -1,6 +1,6 @@ from __future__ import annotations -import typing_extensions +import collections.abc from dataclasses import dataclass from types import NoneType, UnionType from typing_extensions import ( @@ -17,6 +17,7 @@ NotRequired, Protocol, Required, + Self, TypeAlias, TypeAliasType, TypedDict, @@ -95,26 +96,24 @@ class TypeScriptNodeTranslationResult: type_declarations: list[TopLevelDeclarationNode] errors: list[str] - -# TODO: https://github.com/microsoft/pyright/issues/6587 -_SELF_TYPE = getattr(typing_extensions, "Self") - _LIST_TYPES: set[object] = { list, set, frozenset, - # TODO: https://github.com/microsoft/pyright/issues/6582 - # collections.abc.MutableSequence, - # collections.abc.Sequence, - # collections.abc.Set + collections.abc.Sequence, + collections.abc.MutableSequence, + collections.abc.Set, + collections.abc.MutableSet, + collections.abc.Iterable, + collections.abc.Collection, } -# TODO: https://github.com/microsoft/pyright/issues/6582 -# _DICT_TYPES: set[type] = { -# dict, -# collections.abc.MutableMapping, -# collections.abc.Mapping -# } + +_DICT_TYPES: set[type] = { + dict, + collections.abc.MutableMapping, + collections.abc.Mapping, +} def python_type_to_typescript_nodes(root_py_type: object) -> TypeScriptNodeTranslationResult: @@ -186,12 +185,12 @@ def convert_to_type_node(py_type: object) -> TypeNode: return NullTypeReferenceNode if py_type is Never or py_type is NoReturn: return NeverTypeReferenceNode - if py_type is _SELF_TYPE: + if py_type is Self: return ThisTypeReferenceNode # TODO: consider handling bare 'tuple' (and list, etc.) # https://docs.python.org/3/library/typing.html#annotating-tuples - # Using plain tuple as an annotation is equivalent to using tuple[Any, ...]: + # Using plain 'tuple' as an annotation is equivalent to using 'tuple[Any, ...]': origin = get_origin(py_type) if origin is not None: @@ -201,7 +200,7 @@ def convert_to_type_node(py_type: object) -> TypeNode: return TypeReferenceNode(IdentifierNode("Array"), [type_arg]) return ArrayTypeNode(type_arg) - if origin is dict: + if origin in _DICT_TYPES: # TODO # Currently, we naively assume all dicts are string-keyed # unless they're annotated with `int` or `float` (note: not `int | float`). diff --git a/python/tests/abstract_collections.py b/python/tests/abstract_collections.py new file mode 100644 index 00000000..1435f8c0 --- /dev/null +++ b/python/tests/abstract_collections.py @@ -0,0 +1,29 @@ + +from typing import TypedDict +from typechat import python_type_to_typescript_schema + +import collections.abc + +class MyType(TypedDict): + built_in_dict: dict[str, str] + built_in_set: set[str] + built_in_frozen_set: frozenset[str] + + mapping: collections.abc.Mapping[str, str] + mutable_mapping: collections.abc.MutableMapping[str, str] + + set: collections.abc.Set[str] + mutable_set: collections.abc.MutableSet[str] + + sequence: collections.abc.Sequence[str] + mutable_sequence: collections.abc.MutableSequence[str] + +result = python_type_to_typescript_schema(MyType) + +print(f"// Entry point is: '{result.typescript_type_reference}'") +print("// TypeScript Schema:\n") +print(result.typescript_schema_str) +if result.errors: + print("// Errors:") + for err in result.errors: + print(f"// - {err}\n")