{"id":50642508,"url":"https://github.com/ericsssan/ez-checker","last_synced_at":"2026-06-07T10:00:26.091Z","repository":{"id":363059297,"uuid":"1261813900","full_name":"ericsssan/ez-checker","owner":"ericsssan","description":"TypeScript type checker extracted from Ez — per-file inference with cross-file resolution via ModuleResolver","archived":false,"fork":false,"pushed_at":"2026-06-07T09:20:12.000Z","size":10236,"stargazers_count":0,"open_issues_count":9,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-07T09:20:31.527Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Zig","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ericsssan.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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-06-07T07:22:53.000Z","updated_at":"2026-06-07T09:20:16.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ericsssan/ez-checker","commit_stats":null,"previous_names":["ericsssan/ez-checker"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/ericsssan/ez-checker","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericsssan%2Fez-checker","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericsssan%2Fez-checker/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericsssan%2Fez-checker/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericsssan%2Fez-checker/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ericsssan","download_url":"https://codeload.github.com/ericsssan/ez-checker/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ericsssan%2Fez-checker/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34016490,"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-06-07T02:00:07.652Z","response_time":124,"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-06-07T10:00:14.725Z","updated_at":"2026-06-07T10:00:26.082Z","avatar_url":"https://github.com/ericsssan.png","language":"Zig","funding_links":[],"categories":[],"sub_categories":[],"readme":"# ez-checker\n\nA reimplementation of the TypeScript type checker. Just the type system: infer the type of any expression, resolve declarations, narrow through control flow. No emit, no diagnostics, no `tsconfig.json`.\n\nOriginally extracted from the [Ez](https://github.com/ericsssan/Ez) linter, but the type inference engine is general-purpose — usable by linters, compilers, language servers, or any tool that needs to reason about TypeScript types.\n\nBuilt on [es-parser](https://github.com/ericsssan/es-parser). Written in Zig.\n\n---\n\n## What it implements\n\n**Expression inference**\nLiterals, template literals, arrays, objects, functions, classes, binary/unary operators, ternary, assignments, await, sequence, casts (`as`, `satisfies`, `!`).\n\n**Type resolution**\nPrimitives, arrays, tuples, union, intersection, object types, function types, `typeof`, `keyof`, indexed access (`T[K]`), mapped types, conditional types (including `infer`), template literal types, generic type references, recursive type aliases.\n\n**Utility types**\n`Partial`, `Required`, `Readonly`, `Pick`, `Omit`, `Exclude`, `Extract`, `NonNullable`, `Record`, `ReturnType`, `Parameters`, `Awaited`.\n\n**Control-flow narrowing**\nNull/undefined checks, `typeof` guards, `instanceof`, type predicates (`x is T`), assertion functions (`asserts x`), discriminated unions, truthiness, assignment narrowing, early-return narrowing.\n\n**Declarations**\nVariables, functions (with overloads), classes (instance + static, inheritance), interfaces (extends, call/construct signatures, index signatures, declaration merging), type aliases, enums (numeric, string, mixed), namespaces (declaration merging), imports.\n\n**Cross-file resolution**\nOptional. Implement `ModuleResolver` and set `Checker.module_resolver` to resolve types from imported modules lazily.\n\n---\n\n## Known gaps\n\nSee [open issues](https://github.com/ericsssan/ez-checker/issues/9) for the full tracked list. Short version:\n\n- `ConstructorParameters\u003cT\u003e` and `InstanceType\u003cT\u003e` not yet implemented\n- `as const` on object literals (arrays work; objects do not)\n- Optional chaining (`?.`) does not add `| undefined` to the result type\n- `==`/`!=` (loose equality) does not narrow\n- `throw` does not narrow subsequent code\n- Class + interface declaration merging not supported\n- `declare module` / `declare global` augmentation not supported\n- Overload resolution picks the first matching signature, not the best one\n- Global built-in types (~30 hand-curated entries; no full lib.dom or lib.es*)\n\n---\n\n## Usage\n\n`build.zig.zon`:\n```zig\n.dependencies = .{\n    .ez_checker = .{ .path = \"../ez-checker\" },\n    .es_parser  = .{ .path = \"../es-parser\" },\n},\n```\n\n`build.zig`:\n```zig\nconst ez_checker_mod = b.dependency(\"ez_checker\", .{\n    .target = target,\n    .optimize = .ReleaseFast,\n}).module(\"ez-checker\");\n\nyour_module.addImport(\"ez_checker\", ez_checker_mod);\n```\n\n---\n\n## API\n\n```zig\nconst ez = @import(\"ez_checker\");\nconst Checker  = ez.Checker;\nconst types    = ez.types;\n\n// Init from an already-parsed file.\nvar checker = try Checker.init(allocator, \u0026ast, \u0026semantic_result);\ndefer checker.deinit();\n\n// Optional: wire cross-file resolution.\nchecker.file_path      = \"/abs/path/to/file.ts\";\nchecker.module_resolver = my_module_cache.resolver();\n\n// Query the type of any expression node.\nconst ty: types.TypeId = checker.typeOf(node_index);\n\n// Classify the result.\nif (types.isAny(\u0026checker.store, ty))          { /* any */ }\nif (types.containsAny(\u0026checker.store, ty))    { /* any somewhere inside */ }\nif (types.isAssignableTo(\u0026checker.store, src, dst)) { /* assignable */ }\n```\n\n### `ModuleResolver`\n\nImplement this to enable cross-file type resolution:\n\n```zig\nfn resolveImpl(\n    ctx: *anyopaque,\n    from_dir: []const u8,\n    module_spec: []const u8,\n    export_name: []const u8,\n    local_store: *types.TypeStore,\n    gpa: std.mem.Allocator,\n) ?types.TypeId {\n    const cache: *MyCache = @ptrCast(@alignCast(ctx));\n    return cache.resolve(from_dir, module_spec, export_name, local_store, gpa);\n}\n\nchecker.module_resolver = .{\n    .ctx        = @ptrCast(my_cache),\n    .resolve_fn = \u0026resolveImpl,\n};\n```\n\n---\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fericsssan%2Fez-checker","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fericsssan%2Fez-checker","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fericsssan%2Fez-checker/lists"}