Skip to content

Regression: type guard function returning recursive utility type fails to apply the second timeΒ #54246

Closed
@jcalz

Description

@jcalz

Bug Report

πŸ”Ž Search Terms

recursive nested conditional type guard function

πŸ•— Version & Regression Information

  • This changed between versions 5.1.0-dev.20230303 and 5.1.0-dev.20230304

⏯ Playground Link

Playground link with relevant code

πŸ’» Code

type NestedRecord<K extends string, V> =
  K extends `${infer K0}.${infer KR}` ?
  { [P in K0]: NestedRecord<KR, V> } :
  { [P in K]: V };

declare function guardPathX<T>(x: T): x is T & NestedRecord<'a.b.x.c', string>;
declare function guardPathY<T>(x: T): x is T & NestedRecord<'a.b.y.c', string>;

function foo(obj: {}) {
  if (guardPathX(obj) && guardPathY(obj)) {
    obj.a.b.x.c.toUpperCase(); // okay
    obj.a.b.y.c.toUpperCase(); // <-- error, what?
  }
}

πŸ™ Actual behavior

obj has been narrowed only to {a:{b:{x:{c: string}}}}.

πŸ™‚ Expected behavior

obj should be narrowed to {a:{b:{x:{c: string}}}} & {a:{b:{y:{c: string}}}}.


This is a weird one. Seems to be caused by the recursive NestedRecord utility type (if I replace that with hardcoded types it works) interacting with multiple type guard applications. Probably hitting some limit somewhere where it gives up? Or an inaccurate variance shortcut? Not sure. Anyway this broke some Stack Overflow answer I was working on and it would be nice to know what's up with it.

Metadata

Metadata

Assignees

Labels

Design LimitationConstraints of the existing architecture prevent this from being fixed

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions