Skip to content

Python to TypeScript Type Mapping

This page describes the TypeScript shapes tywrap generates and returns today.

Core Annotation Mapping

Primitives

PythonTypeScript
intnumber
floatnumber
strstring
boolboolean
bytesstring
None in value positionnull
None in return positionvoid

Collections

PythonTypeScriptNotes
list[T], List[T]Array<T>
Sequence[T]Array<T>Normalized to a list-like shape
tuple[T1, T2][T1, T2]Exact tuple shape
Tuple[T, ...]Array<T>Variable-length tuple
tuple[()][undefined]Empty tuple representation
set[T], frozenset[T]Set<T>
dict[K, V], Dict[K, V]{ [key: K]: V }Index keys fall back to string when needed
Mapping[K, V]{ [key: K]: V }Normalized to a dict-like shape

Unions and Literals

PythonTypeScript
Union[A, B]A | B
A | BA | B
Optional[T]T | null
T | NoneT | null
Literal["a", "b"]"a" | "b"
Literal[1, 2]1 | 2
Literal[True, False]true | false

Callables and Wrappers

PythonTypeScriptNotes
Callable[[A, B], R](arg0: A, arg1: B) => R
Callable[..., R](...args: unknown[]) => R
Annotated[T, ...]TMetadata is stripped for the generated type
ClassVar[T]T
Final[T]T
Required[T], NotRequired[T]T

Special Names

PythonTypeScriptNotes
Anyunknown
Never, NoReturnnever
LiteralString, AnyStrstring
objectobject
AwaitablePromise<unknown>
CoroutinePromise<unknown>
TypeVar('T')TPreserved for simple unconstrained/invariant declarations; otherwise falls back to unknown
ParamSpec('P')P extends unknown[]Preserved for callable parameter packs when tywrap can emit a matching generic declaration
TypeVarTuple('Ts')unknownVariadic generic parameters still fall back conservatively
Unpack[Ts]unknownVariadic tuple unpacking still falls back conservatively

Preset Mappings

Enable presets with types.presets in your config.

stdlib

PythonTypeScript
datetime.datetime, datetime.date, datetime.timestring
datetime.timedeltanumber
decimal.Decimalstring
uuid.UUIDstring
pathlib.Path and related path typesstring

pandas

These are the decoded JavaScript shapes that tywrap exposes to TypeScript.

PythonTypeScript
pandas.DataFrameRecord<string, unknown> | Array<Record<string, unknown>>
pandas.Seriesunknown[] | Record<string, unknown>

pydantic

PythonTypeScriptNotes
pydantic.BaseModelRecord<string, unknown>Runtime serialization uses model_dump(by_alias=True, mode='json') when available

scipy

ts
type SparseMatrix =
  | {
      format: 'csr' | 'csc';
      shape: number[];
      data: unknown[];
      indices: number[];
      indptr: number[];
      dtype?: string;
    }
  | {
      format: 'coo';
      shape: number[];
      data: unknown[];
      row: number[];
      col: number[];
      dtype?: string;
    };

torch

ts
interface TorchTensor {
  data: unknown;
  shape: number[];
  dtype?: string;
  device?: string;
}

sklearn

ts
interface SklearnEstimator {
  className: string;
  module: string;
  version?: string;
  params: Record<string, unknown>;
}

Limits and Fallbacks

  • tywrap does not preserve Python numeric distinctions beyond number.
  • tywrap preserves simple unconstrained TypeVars, generic classes, generic type aliases, and callable ParamSpec packs in generated .ts and .d.ts output when they can be rendered safely.
  • Complex user-defined generics that are not explicitly normalized stay as custom TypeScript names.
  • Bound, constrained, or variant TypeVars, plus variadic generics such as TypeVarTuple and Unpack, still lower conservatively to unknown-based shapes.
  • P.args and P.kwargs lower conservatively in runtime wrapper signatures as unknown[] and Record<string, unknown>.
  • Annotated[...] metadata is not reflected in the generated type.
  • Runtime serialization can still shape values more narrowly than the static annotation alone. torch.Tensor and sklearn values are examples of this.

Generic Example

python
from typing import Callable, Generic, TypeVar
try:
    from typing import ParamSpec
except ImportError:
    from typing_extensions import ParamSpec

T = TypeVar("T")
P = ParamSpec("P")

Pair = tuple[T, T]
Transform = Callable[P, T]

class Container(Generic[T]):
    def get(self) -> T:
        ...

Generates TypeScript shaped like:

ts
export type Pair<T> = [T, T];
export type Transform<P extends unknown[], T> = (...args: P) => T;
export class Container<T> {
  get(): Promise<T>;
}

Basic Example

python
from typing import Literal, Optional, Sequence

def summarize(values: Sequence[int], mode: Literal["min", "max"]) -> Optional[int]:
    ...

Generates a TypeScript signature shaped like:

ts
export async function summarize(
  values: Array<number>,
  mode: 'min' | 'max'
): Promise<number | null>;

Released under the MIT License.