Skip to content

7.1 regression: declaration order of recursive generic interfaces changes assignability (TS2322) #64891

Open

Description

🔎 Search Terms

variance, circular variance, declaration order, mapped type as clause, key remapping, TS2322, 7.1 regression

🕗 Version & Regression Information

  • Last good: 7.1.0-dev.20260805.1 (gitHead 12318e599d)
  • First bad: 7.1.0-dev.20260806.1 (gitHead 86cc4767d4)
  • That range is one commit: Order variance computation by associated type symbol typescript-go#4820, "Order variance computation by associated type symbol".
  • Still failing on 7.1.0-beta and 7.1.0-dev.20261020.3.
  • TypeScript 6.0.3 and 7.0.2 accept the code.

⏯ Playground Link

The tested 7.1 builds aren't available in the Playground: requesting 7.1.0-dev.20261020.3 reports that it hasn't been prepared, and Nightly currently loads 6.0.0-dev.20260416. The CLI repro is two files in an empty directory, with no package.json, run with tsc -p .:

tsconfig.json

{ "compilerOptions": { "noEmit": true, "strict": true }, "files": ["repro.ts"] }
Version tsc -p .
6.0.3 no errors
7.0.2 no errors
7.1.0-dev.20260805.1 no errors
7.1.0-dev.20260806.1 TS2322
7.1.0-beta TS2322
7.1.0-dev.20261020.3 TS2322

In my runs the result did not change with strict off, or with module/moduleResolution set to nodenext or to esnext/bundler.

💻 Code

repro.ts

type M = { kind: "m" };
type Filtered<T> = { [K in keyof T as T[K] extends M ? K : never]: T[K] };
interface Config<T> { measure: keyof T }
interface Ref<N> { dataset: DS<Record<string, M>, N> }
interface DS<T, N> {
  a(config: Config<Filtered<T>>): void;
  b: Ref<N>;
}
declare const x: DS<{ total: M }, string>;
export const y: DS<Record<string, M>, string> = x;

🙁 Actual behavior

repro.ts(10,14): error TS2322: Type 'DS<{ total: M; }, string>' is not assignable to type 'DS<Record<string, M>, string>'.
  Property 'total' is missing in type 'Record<string, M>' but required in type '{ total: M; }'.

The reverse assignment (DS<Record<string, M>, string> to DS<{ total: M }, string>) also fails. This is consistent with T being treated as invariant. The diagnostic reports the missing total property on the type argument rather than an incompatibility in a or b. I haven't traced whether a structural comparison is attempted.

🙂 Expected behavior

No error, as in 6.0.3 and 7.0.2. The assignment is valid member by member. a is a method, so its parameter is checked bivariantly ({ measure: "total" } against { measure: string }). b is the same type on both sides. Replacing the final assignment with this member-by-member version succeeds on both tested 7.1 builds:

export const y: DS<Record<string, M>, string> = { a: x.a, b: x.b };

⏯ Additional information about the issue

The result depends on declaration order. Each of these changes, applied on its own to the repro above, makes the assignment pass on both 7.1.0-beta and 7.1.0-dev.20261020.3:

  1. Declaring DS before Ref.
  2. Adding this comparison before the failing assignment:
    declare const r: Ref<"a">;
    export const r2: Ref<string> = r;
  3. Writing a's parameter as { measure: keyof Filtered<T> } instead of Config<Filtered<T>>.
  4. Dropping the as clause from Filtered.
  5. Removing N from DS, Ref and their instantiations, so that Ref is not generic.

DS.b doesn't mention T. Ref<N>.dataset always fixes DS's first argument to Record<string, M> and passes through only N. Even so, swapping the declarations changes whether the assignment succeeds.

Real-world impact: this is reduced from @hypequery/datasets 0.23.1. A dataset created with named total and count measures assigns to the exported AnyDatasetInstance on 6.0.3 and 7.0.2, but fails with TS2322 on the tested 7.1 builds. DatasetInstance.metric() takes BaseMetricConfig<BaseMeasures<TMeasures>>, where BaseMeasures uses key remapping. Its returned references carry the concrete dataset type. Separately, DerivedMetricConfig.uses holds BaseMetricRef<TDatasetName> references whose default dataset type is the wide one.

Activity

  1. changed the title [-]7.1 regression: variance of a type parameter depends on declaration order of an unrelated circular pair, giving a false TS2322[/-] [+]7.1 regression: declaration order of recursive generic interfaces changes assignability (TS2322)[/+] on Oct 21, 2026
  2. shahinsamiur commented on Oct 21, 2026

    @shahinsamiur

    Hello! I am currently investigating this problem and developing a solution to the variance calculation implementation changes introduced in the referenced commit. I will be updating here when I have any updates or PR ready.

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

Metadata

Metadata

Labels

Needs InvestigationThis issue needs a team member to investigate its status.

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions