{"id":44957090,"url":"https://github.com/openrewrite/ty-types","last_synced_at":"2026-05-27T09:00:40.767Z","repository":{"id":339163889,"uuid":"1160750366","full_name":"openrewrite/ty-types","owner":"openrewrite","description":"Expose ty's type inference as a CLI tool and JSON-RPC server.","archived":false,"fork":false,"pushed_at":"2026-04-20T07:26:07.000Z","size":104630,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-20T09:28:38.627Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/openrewrite.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-02-18T10:26:17.000Z","updated_at":"2026-04-20T07:22:05.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/openrewrite/ty-types","commit_stats":null,"previous_names":["openrewrite/ty-types"],"tags_count":5,"template":false,"template_full_name":null,"purl":"pkg:github/openrewrite/ty-types","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openrewrite%2Fty-types","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openrewrite%2Fty-types/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openrewrite%2Fty-types/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openrewrite%2Fty-types/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/openrewrite","download_url":"https://codeload.github.com/openrewrite/ty-types/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openrewrite%2Fty-types/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33558664,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-05-27T02:00:06.184Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-02-18T12:07:26.691Z","updated_at":"2026-05-27T09:00:40.757Z","avatar_url":"https://github.com/openrewrite.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ty-types\n\nA Rust CLI that exposes [ty](https://github.com/astral-sh/ty)'s Python type inference as structured JSON. It can infer types for one or more files in a single invocation, or run as a JSON-RPC server over stdio for multi-file sessions with cross-request type deduplication.\n\n## Building\n\nRequires Rust 1.93+. The `ruff/` submodule must be checked out first:\n\n```bash\ngit submodule update --init\ncargo build --release\n```\n\nThe binary is at `target/release/ty-types`.\n\n## Usage\n\n### One-shot mode\n\nPass one or more Python files as arguments. The output is a single JSON object written to stdout:\n\n```bash\nty-types app.py utils.py --project-root /path/to/project\n```\n\nIf `--project-root` is omitted, it defaults to the parent directory of the first file.\n\n**Output format:**\n\n```json\n{\n  \"files\": {\n    \"/absolute/path/to/app.py\": [ \u003cNodeAttribution\u003e, ... ],\n    \"/absolute/path/to/utils.py\": [ \u003cNodeAttribution\u003e, ... ]\n  },\n  \"types\": {\n    \"1\": \u003cTypeDescriptor\u003e,\n    \"2\": \u003cTypeDescriptor\u003e\n  }\n}\n```\n\n`files` maps each file path to its list of typed AST nodes. `types` is a shared registry — nodes reference types by ID, and the same type (e.g. `int`) gets a single entry even if it appears in multiple files.\n\n### JSON-RPC server mode\n\nFor processing many files or integrating with editors and tooling, run as a persistent server:\n\n```bash\nty-types --serve\n```\n\nThe server reads JSON-RPC requests from stdin (one per line) and writes responses to stdout. A session looks like:\n\n```\n→ {\"jsonrpc\":\"2.0\",\"method\":\"initialize\",\"params\":{\"projectRoot\":\"/path/to/project\"},\"id\":1}\n← {\"jsonrpc\":\"2.0\",\"result\":{\"ok\":true},\"id\":1}\n\n→ {\"jsonrpc\":\"2.0\",\"method\":\"getTypes\",\"params\":{\"file\":\"app.py\"},\"id\":2}\n← {\"jsonrpc\":\"2.0\",\"result\":{\"nodes\":[...],\"types\":{\"1\":...,\"2\":...}},\"id\":2}\n\n→ {\"jsonrpc\":\"2.0\",\"method\":\"getTypes\",\"params\":{\"file\":\"utils.py\"},\"id\":3}\n← {\"jsonrpc\":\"2.0\",\"result\":{\"nodes\":[...],\"types\":{\"5\":...}},\"id\":3}\n\n→ {\"jsonrpc\":\"2.0\",\"method\":\"shutdown\",\"id\":99}\n← {\"jsonrpc\":\"2.0\",\"result\":{\"ok\":true},\"id\":99}\n```\n\nThe type registry persists across `getTypes` requests within a session. Each `getTypes` response includes only the *newly discovered* types — types already sent in a previous response are not repeated. The client accumulates the registry as it goes.\n\nTo retrieve the full accumulated registry at any point, call `getTypeRegistry`.\n\n## JSON-RPC methods\n\n### `initialize`\n\nMust be the first call. Creates the project database.\n\n| Field | Type | Description |\n|---|---|---|\n| `params.projectRoot` | `string` | Absolute path to the Python project root |\n\nReturns `{\"ok\": true}`.\n\n### `getTypes`\n\nInfers types for a Python file and returns the typed AST nodes plus any new type descriptors.\n\n| Field | Type | Default | Description |\n|---|---|---|---|\n| `params.file` | `string` | | File path (absolute or relative to project root) |\n| `params.includeDisplay` | `boolean` | `true` | Include human-readable `display` strings on type descriptors |\n\nReturns:\n\n```json\n{\n  \"nodes\": [ \u003cNodeAttribution\u003e, ... ],\n  \"types\": { \"\u003cTypeId\u003e\": \u003cTypeDescriptor\u003e, ... }\n}\n```\n\n### `getTypeRegistry`\n\nReturns the full accumulated type registry from all `getTypes` calls in the current session. Takes no parameters.\n\nReturns:\n\n```json\n{\n  \"types\": { \"\u003cTypeId\u003e\": \u003cTypeDescriptor\u003e, ... }\n}\n```\n\n### `shutdown`\n\nEnds the session and exits the server. Returns `{\"ok\": true}`.\n\n## Schema\n\n### NodeAttribution\n\nEach entry in the `nodes` array represents a typed AST node:\n\n```json\n{\n  \"start\": 0,\n  \"end\": 5,\n  \"nodeKind\": \"ExprName\",\n  \"typeId\": 1,\n  \"callSignature\": null\n}\n```\n\n| Field | Type | Description |\n|---|---|---|\n| `start` | `integer` | Byte offset of the node start in the source file |\n| `end` | `integer` | Byte offset of the node end |\n| `nodeKind` | `string` | AST node kind (see below) |\n| `typeId` | `integer \\| null` | Reference into the type registry |\n| `callSignature` | `CallSignatureInfo \\| null` | Present only on `ExprCall` nodes |\n\n**Node kinds:** `StmtFunctionDef`, `StmtClassDef`, `StmtAssign`, `StmtFor`, `StmtWith`, `ExprCall`, `ExprBoolOp`, `ExprBinOp`, `ExprUnaryOp`, `ExprLambda`, `ExprIf`, `ExprDict`, `ExprSet`, `ExprListComp`, `ExprSetComp`, `ExprDictComp`, `ExprGenerator`, `ExprAwait`, `ExprYield`, `ExprYieldFrom`, `ExprCompare`, `ExprFString`, `ExprTString`, `ExprStringLiteral`, `ExprBytesLiteral`, `ExprNumberLiteral`, `ExprBooleanLiteral`, `ExprNoneLiteral`, `ExprEllipsisLiteral`, `ExprAttribute`, `ExprSubscript`, `ExprStarred`, `ExprName`, `ExprList`, `ExprTuple`, `ExprSlice`, `Parameter`, `ParameterWithDefault`, `Alias`\n\n### CallSignatureInfo\n\nAttached to `ExprCall` nodes. Contains the resolved signature at the call site, including any generic specialization:\n\n```json\n{\n  \"parameters\": [ \u003cParameterInfo\u003e, ... ],\n  \"returnTypeId\": 3,\n  \"typeArguments\": [4]\n}\n```\n\n| Field | Type | Description |\n|---|---|---|\n| `parameters` | `ParameterInfo[]` | Resolved parameters of the called function |\n| `returnTypeId` | `integer \\| null` | Return type (specialized if generic) |\n| `typeArguments` | `integer[]` | Type arguments inferred for generic calls (e.g. `T=int`) |\n\n### ParameterInfo\n\n```json\n{\n  \"name\": \"x\",\n  \"typeId\": 2,\n  \"kind\": \"positionalOrKeyword\",\n  \"hasDefault\": true,\n  \"defaultTypeId\": 5\n}\n```\n\n| Field | Type | Description |\n|---|---|---|\n| `name` | `string` | Parameter name |\n| `typeId` | `integer \\| null` | Annotated type |\n| `kind` | `string` | One of `positionalOnly`, `positionalOrKeyword`, `keywordOnly`, `variadic`, `keywordVariadic` |\n| `hasDefault` | `boolean` | Whether the parameter has a default value |\n| `defaultTypeId` | `integer \\| null` | Type of the default value (e.g. `Literal[42]`) |\n| `concatenatePrefix` | `boolean` | `true` on the leading positional parameters of a `Concatenate[T1, ..., Tn, P]` or `Concatenate[T1, ..., Tn, ...]` signature *(omitted when false)* |\n| `paramSpecName` | `string` | Set on the `*args` / `**kwargs` entries that stand in for a `ParamSpec` tail, carrying that `ParamSpec`'s name (e.g. `\"P\"`) *(omitted when absent)* |\n\n### TypeDescriptor\n\nEvery type in the registry is a tagged object with a `\"kind\"` discriminator. All variants include an optional `display` field with ty's human-readable representation (omit with `includeDisplay: false`).\n\nFields marked with *\"omitted when empty\"* are not present in the JSON when their value is empty or null.\n\n#### `instance`\n\nAn object of a class (e.g. `int`, `str`, `MyClass()`).\n\n| Field | Type | Description |\n|---|---|---|\n| `className` | `string` | Class name |\n| `moduleName` | `string` | Defining module *(omitted when empty)* |\n| `supertypes` | `integer[]` | Resolved base class type IDs *(omitted when empty)* |\n| `typeArgs` | `integer[]` | Specialization args, e.g. `list[int]` → `[\u003cint\u003e]` *(omitted when empty)* |\n| `classId` | `integer` | Type ID of the corresponding `classLiteral` *(omitted when empty)* |\n\n#### `classLiteral`\n\nA class object itself (the value of `type[MyClass]`).\n\n| Field | Type | Description |\n|---|---|---|\n| `className` | `string` | Class name |\n| `moduleName` | `string` | Defining module *(omitted when empty)* |\n| `typeParameters` | `integer[]` | Generic type parameters (`T`, `U`, ...) *(omitted when empty)* |\n| `supertypes` | `integer[]` | Explicit base classes *(omitted when empty)* |\n| `members` | `ClassMemberInfo[]` | Directly defined class members *(omitted when empty)* |\n\n`ClassMemberInfo`: `{ \"name\": string, \"typeId\": integer }`\n\n#### `subclassOf`\n\nA `type[C]` constraint (subclass relationship).\n\n| Field | Type | Description |\n|---|---|---|\n| `base` | `integer` | Type ID of the base `classLiteral` |\n\n#### `union`\n\nA union type (`X | Y`).\n\n| Field | Type | Description |\n|---|---|---|\n| `members` | `integer[]` | Type IDs of the union members |\n\n#### `intersection`\n\nA narrowed type from control flow (e.g. `isinstance` checks).\n\n| Field | Type | Description |\n|---|---|---|\n| `positive` | `integer[]` | Types that must all be satisfied |\n| `negative` | `integer[]` | Types that must not be satisfied |\n\n#### `function`\n\nA named function.\n\n| Field | Type | Description |\n|---|---|---|\n| `name` | `string` | Function name |\n| `moduleName` | `string` | Defining module *(omitted when empty)* |\n| `typeParameters` | `integer[]` | Generic type parameters *(omitted when empty)* |\n| `parameters` | `ParameterInfo[]` | Full signature |\n| `returnType` | `integer \\| null` | Return type ID |\n\n#### `boundMethod`\n\nA method bound to an instance.\n\n| Field | Type | Description |\n|---|---|---|\n| `name` | `string \\| null` | Method name *(omitted when empty)* |\n| `moduleName` | `string \\| null` | Defining module *(omitted when empty)* |\n| `typeParameters` | `integer[]` | Generic type parameters *(omitted when empty)* |\n| `parameters` | `ParameterInfo[]` | Full signature (without `self`) |\n| `returnType` | `integer \\| null` | Return type ID |\n\n#### `callable`\n\nA generic callable with unknown signature.\n\nNo additional fields beyond `display`.\n\n#### `intLiteral`\n\n| Field | Type | Description |\n|---|---|---|\n| `value` | `integer` | The literal value |\n\n#### `boolLiteral`\n\n| Field | Type | Description |\n|---|---|---|\n| `value` | `boolean` | `true` or `false` |\n\n#### `stringLiteral`\n\n| Field | Type | Description |\n|---|---|---|\n| `value` | `string` | The literal string value |\n\n#### `bytesLiteral`\n\n| Field | Type | Description |\n|---|---|---|\n| `value` | `string` | Display representation (e.g. `Literal[b\"data\"]`) |\n\n#### `enumLiteral`\n\n| Field | Type | Description |\n|---|---|---|\n| `className` | `string` | Enum class name |\n| `memberName` | `string` | Member name |\n\n#### `literalString`\n\nThe `typing.LiteralString` special form. No additional fields.\n\n#### `typeVar`\n\nA generic type variable.\n\n| Field | Type | Description |\n|---|---|---|\n| `name` | `string` | Variable name (e.g. `T`) |\n| `variance` | `string \\| null` | `covariant`, `contravariant`, or `invariant` *(omitted when empty)* |\n| `upperBound` | `integer \\| null` | Bound type ID (from `T: bound=int`) *(omitted when empty)* |\n| `constraints` | `integer[]` | Constraint type IDs (from `T(int, str)`) *(omitted when empty)* |\n\n#### `module`\n\n| Field | Type | Description |\n|---|---|---|\n| `moduleName` | `string` | Fully qualified module name |\n\n#### `typeAlias`\n\n| Field | Type | Description |\n|---|---|---|\n| `name` | `string` | Alias name |\n\n#### `typedDict`\n\n| Field | Type | Description |\n|---|---|---|\n| `name` | `string` | TypedDict name |\n| `fields` | `TypedDictFieldInfo[]` | Typed fields *(omitted when empty)* |\n\n`TypedDictFieldInfo`: `{ \"name\": string, \"typeId\": integer, \"required\": boolean, \"readOnly\": boolean }`\n\n#### `typeIs`\n\nA `TypeIs[T]` return type for type narrowing functions.\n\n| Field | Type | Description |\n|---|---|---|\n| `narrowedType` | `integer` | The narrowed type ID |\n\n#### `typeGuard`\n\nA `TypeGuard[T]` return type for type guard functions.\n\n| Field | Type | Description |\n|---|---|---|\n| `guardedType` | `integer` | The guarded type ID |\n\n#### `newType`\n\n| Field | Type | Description |\n|---|---|---|\n| `name` | `string` | NewType name |\n| `baseType` | `integer` | Underlying type ID |\n\n#### `specialForm`\n\nTyping special forms like `Any`, `Never`, `ClassVar`, etc.\n\n| Field | Type | Description |\n|---|---|---|\n| `name` | `string` | Form name |\n\n#### `dynamic`\n\nUnknown or dynamically typed values.\n\n| Field | Type | Description |\n|---|---|---|\n| `dynamicKind` | `string` | e.g. `Unknown` |\n\n#### `never`\n\nThe bottom type (unreachable code). No additional fields.\n\n#### `truthy` / `falsy`\n\nNarrowed truthiness. No additional fields.\n\n#### `property`\n\nA property descriptor. No additional fields.\n\n#### `other`\n\nFallback for ty-internal types not yet mapped to a structured descriptor. No additional fields beyond `display`.\n\n## Example\n\nGiven `example.py`:\n\n```python\nx: int = 42\n```\n\n**One-shot:**\n\n```bash\nty-types example.py\n```\n\n```json\n{\n  \"files\": {\n    \"/path/to/example.py\": [\n      { \"start\": 0, \"end\": 1, \"nodeKind\": \"ExprName\", \"typeId\": 1 },\n      { \"start\": 9, \"end\": 11, \"nodeKind\": \"ExprNumberLiteral\", \"typeId\": 2 }\n    ]\n  },\n  \"types\": {\n    \"1\": { \"kind\": \"instance\", \"display\": \"int\", \"className\": \"int\" },\n    \"2\": { \"kind\": \"intLiteral\", \"display\": \"Literal[42]\", \"value\": 42 }\n  }\n}\n```\n\n**Server mode** (processing two files across requests):\n\n```bash\necho '{\"jsonrpc\":\"2.0\",\"method\":\"initialize\",\"params\":{\"projectRoot\":\"/path/to/project\"},\"id\":1}\n{\"jsonrpc\":\"2.0\",\"method\":\"getTypes\",\"params\":{\"file\":\"example.py\"},\"id\":2}\n{\"jsonrpc\":\"2.0\",\"method\":\"getTypes\",\"params\":{\"file\":\"other.py\"},\"id\":3}\n{\"jsonrpc\":\"2.0\",\"method\":\"shutdown\",\"id\":99}' | ty-types --serve\n```\n\nThe response for `id:2` includes type descriptors for `int` and `Literal[42]`. If `other.py` also uses `int`, the response for `id:3` will *not* repeat the `int` descriptor — it was already sent. Call `getTypeRegistry` at any point to get the full accumulated registry.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenrewrite%2Fty-types","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopenrewrite%2Fty-types","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenrewrite%2Fty-types/lists"}