Skip to content

Type parameter escapes its constraint in a recursive call resolution #64529

Description

🔎 Search Terms

recursive, constraint, satisfies, return type inference, 64311

🕗 Version & Regression Information

⏯ Playground Link

No response

💻 Code

interface Schema<O> { readonly out: O }
type Shape = Record<string, Schema<any>>;
type Lookup = { x: 1; dflt: 2 };
declare function object<S extends Shape, P extends keyof Lookup>(shape: S): Schema<{ [K in keyof S]: S[K]["out"] }> & { p: P; f: (x: P) => void };
declare const str: Schema<string>;

const u = object({ name: str }) satisfies { p: number | "x" };
u.f(42); // error, P is "x"

const t = object({ name: str, get rec() { return object({ inner: t }); } }) satisfies { p: number | "x" };
t.f(42); // no error
const tp: "x" = t.p; // error: Type 'number | "x"' is not assignable to type '"x"'

🙁 Actual behavior

P on t is number | "x", outside keyof Lookup, with no diagnostic. P only has a return-type inference from satisfies. The outer resolution filters it by the constraint, the re-entrant one doesn't, and typeof t ends up with the re-entrant result. With two mutually recursive consts only the one that resolves first gets the leaked type, so swapping the declaration order moves the error. Wes raised this in review: #64311 (review)

🙂 Expected behavior

P is "x" on t as it is on u, and t.f(42) is an error.

Additional information about the issue

No response

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