Skip to content

declaration emit depends on checker assignment; a type alias becomes any with no error #64998

Description

@blickly

🔎 Search Terms

singleThreaded, --checkers, checker pool, checkers declaration any, nondeterministic declaration, circularly references itself keyof, TS2456

🕗 Version & Regression Information

  • This is the behavior in every version multi-threaded I tried, back to 7.0

⏯ Playground Link

https://tsgo.sxzz.dev/#eNqFU01vnDAQ/SsjnxJpPy49OdpDlSjVtsq2Slv1gDlQGHbdGBv5Y1u05b93jFkETaVyYfB7896bAS6sZjzLWGm0Cw3ajXdsxbZbuD9h+QKVdH47YRWhXGhC1/CAdRGUh5s3UEYqWnfLgS781RrrocJSFRYhNnsog7WoPYdCd3ejwjfpT7BeO6mPCr+cLBYVVvy/CrKJ6I1gm63zhQ9OsNvN56Ei5YTCBRL2aE3z9tMeeqipgnkTkYUerRYWsFs2Z4I9fz0c9od3guXUxfJVxhIjbWv09F2LZPwBu7ldPP3bTRXOQYp8P9QXoQFGEz4iFGNmfBcZ2Qt2nMJZWll+5RHSz8SHFJMCNZh6YfWbVP81/GJkTnMkM5pmcqKxdnDprysYJksbmHvH+XdjyInqyKSWx80PZzTxh3kFfXRNKxXaj62XFEIwnjYRMV/YI/p4JBg6ykr16go2pgoKE9hadGjP+Ap+RmdUiMqJ+D3oisxmvBiyHDy8DTgdo3saBPbaozXtK9wE/yBtEo0/yEwxfbPF1XTRZmOeM76nFTxN+SMjEugV9izPV6xknNGGznTT8njyqqOnn4zXhXLY/wEEuD5q

💻 Code

status.ts

import type { Key } from "./types";
 
export class StatusClass {
  RUNNING: Status = "RUNNING";
  [key: string]: Status;
}
 
export type Status = keyof StatusClass | "";
 
export const statusFromAPI: { [key: Key]: Status } = {};

consumer.ts

import { statusFromAPI } from "./status";
 
export const current = statusFromAPI["RUNNING"];

types.ts:

export type Key = string;

🙁 Actual behavior

The consumer.d.ts output gets incorrectly inferred as any:

export declare const current: any;

🙂 Expected behavior

Both TS6 and --singleThreaded emit the correct type:

export declare const current: import("./status").Status;

Additional information about the issue

The emitted type depends on how many checkers there are, which really means how the files are split across them. Status below means import("./status").Status. Every run exits 0 with no diagnostics:

Version --singleThreaded / --checkers 1 --checkers 2 --checkers 3 default (4) --checkers 8
6.0.3 (one checker) Status n/a n/a n/a n/a
7.0.2 Status any any any any
7.1.0-beta Status Status any any any
7.1.0-dev.20261028 Status Status any any any

Each configuration gives the same result on every run (10 of 10 runs of the 7.1.0-beta default were identical), so this comes from how files are partitioned, not from a race. The .d.ts silently loses the type, and the build gives no sign that anything went wrong.

Activity

  1. blickly commented on Nov 7, 2026

    @blickly
    ContributorAuthor

    Even though it's not what I saw in "the wild" for this bug, the same issue can be extended pretty easily to a case where running with a single checker correctly catches a type error, and running with 3 or more checkers fails to find the error.

    e.g.

    import { statusFromAPI } from "./status";
    
    export const n: boolean = statusFromAPI["RUNNING"];

    gives a:

    consumer.ts(3,14): error TS2322: Type 'string' is not assignable to type 'boolean'.
    

    diagnostic in TS6 / tsgo --singleThreaded / etc., but fails to find the error when checking with 3 or more checkers.

  2. RyanCavanaugh commented on Nov 10, 2026

    @RyanCavanaugh
    Member

    Should be fixed by #65293

  3. jakebailey commented on Nov 10, 2026

    @jakebailey
    Member

    Sadly Ben Lickly (@blickly) already confirmed it does not, which is scary

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    BugA bug in TypeScript

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions