Skip to content

Reordering variable declarations of Promise<any> and Promise<unknown> changes behaviorΒ #52100

Open

Description

Bug Report

πŸ”Ž Search Terms

Promise<any> Promise<unknown> instantiation order

πŸ•— Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

// Reorder these two
declare let u: Promise<unknown>
declare let a: Promise<any>

// Assigned an expression that is Promise<any> | Promise<unknown>
// but that will undergo subtype reduction
// The type of union depends on whether a or u was declared first
let union = Math.random() > 0.5
    ? Promise.reject<any>()
    : Promise.reject<unknown>();

union.then(v => v.toString());

πŸ™ Actual behavior

Reordering the unrelated variables u and a will cause the type of union to change between Promise<unknown> and Promise<any> (both in declarations and in the type of v, although tooltips still show union as Promise<any>)

πŸ™‚ Expected behavior

The type of union should not be impacted

Activity

  1. fatcerberus commented on Jan 4, 2023

    @fatcerberus

    I’m thinking TS probably considers Promise<any> and Promise<unknown> to be subtypes of each other (subtyping is not a total order, from what I understand), so subtype reduction is order-dependent.

  2. dragomirtitian commented on Jan 4, 2023

    @dragomirtitian
    ContributorAuthor

    Bruce Pascoe (@fatcerberus) That is my conclusion as well. Promise<any> and Promise<unknown> are subtypes of each other. When reducing the union it matters in what order the constituents are in the union because that will determine what question is considered (Promise<any> extends Promise<unknown> and Promise<unknown> extends Promise<any> are both true ) And the order of union constituents is determined by the id of the type which is assigned in the order types are encountered.

  3. robpalme commented on Jan 10, 2023

    @robpalme

    Given the conclusion on ordering issues in the 6th Jan Design Meeting...

    Don't really have any solution here. Many similar issues show up.

    This implies that any direct user of the checker API needs to ensure that the order of source files fed to TypeScript is deterministic and, crucially, matches the order used by tsc and the VS Code Language Service Plugin (LSP).

    The file load order is a significant piece of knowledge that anyone embedding the TypeScript checker in a build tool needs to know and replicate. Otherwise the types and checking errors reported may differ between the IDE/LSP and the build tool.

    Ideal Solution

    Ideally, the TypeScript language would make type declaration ordering irrelevant. That is easiest for users to understand and, as a bonus, permits the most flexibility and performance. But it sounds like that is not on the table right now.

    A second-best solution could be to make it possible to ban or lint-away any usage of the language that incurs ordering dependencies, so that projects could opt-into the subset of the language where ordering doesn't matter.

    Fallback Solution

    Today, we embed the TypeScript checker in a custom toolchain that is used locally and during a remote project publishing step. But we don't have a custom LSP - we reuse the default VS Code LSP and configure it via a generated tsconfig written to the source directory. So for true consistency, it appears that we need to update the toolchain to ensure that we are matching the canonical TypeScript file load order.

    Is this canonical load order specified anywhere? Is there a way to reuse TypeScript's existing logic here? It would be great to reuse that code so that if TypeScript ever changes the order, our toolchain continues to match.

  4. RyanCavanaugh commented on Jan 13, 2023

    @RyanCavanaugh
    Member

    Is this canonical load order specified anywhere?

    I'll give a rough sketch. The order is:

    • Lib files
    • The files array, in its original order
    • The include array, the constituents of which I believe are alphabetically sorted (not sure) on a per-pattern basis

    This order is not final; it's then stably sorted by the dependency graph implied by reference directives and import directives.

  5. dragomirtitian commented on Feb 23, 2023

    @dragomirtitian
    ContributorAuthor

    Ryan Cavanaugh (@RyanCavanaugh) This issue now appears to not happen anymore, and inference has stabilized on Promise<any>. This happened between 5.0.0-dev.20230210 and 5.0.0-dev.20230216 (can't pin point more accurately as playground has a gap of version between those dates)

    Was this intentional? Is this going to remain so going forward?

  6. RyanCavanaugh commented on Feb 23, 2023

    @RyanCavanaugh
    Member

    AFAIK, we haven't done anything which we believed to be a fix.

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

    Needs ProposalThis issue needs a plan that clarifies the finer details of how it could be implemented.SuggestionAn idea for TypeScript

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions