Skip to content

Circular recursive types in a mapped-type and conditional-type world #23400

@krryan

Description

@krryan

In attempting to produce a reasonable facsimile of the desired PathIn type from #23398, I ran into this issue with the block on circular type references that I don't think has a workaround, unlike previous issues raised on the subject. #14174 seems closest, and the discussion there identified a related use-case for allowing circular references on types, but I think it makes more sense to raise these concerns as a separate issue rather than extend that discussion (which isn't that closely related to the original issue anyway).

The issue, in short, is that types cannot handle circular references, while classes and interfaces (which can) don't support distribution over a union or being a union.

TypeScript Version: 2.9.0-dev.20180412

Search Terms: circular reference

Code
(strange type Blah<A extends string> = A extends string ? /*...*/ : never; used to force distribution)

type PathBase<A extends string> = A extends string ? {
    node: A;
}: never;
export type Leaf<A extends string> = A extends string ? {
    kind: 'leaf';
    node: A;
}: never;
export type Branch<A extends string, B extends PathBase<string>> = A extends string ? {
    kind: 'branch',
    node: A;
    next: B;
}: never;

export type PathIn<Obj extends {}> =
/*          ^^^^^^
[ts] Type alias 'PathIn' circularly references itself. */
    Obj[keyof Obj] extends {}
        ? Leaf<keyof Obj> | Branch<keyof Obj, PathIn<Obj[keyof Obj]>>
/*                                            ^^^^^^^^^^^^^^^^^^^^^^
[ts] Type 'PathIn' is not generic. */
        : Leaf<keyof Obj>;

Expected behavior:
Compiles without error. PathIn produces a wild and crazy union of linked lists using Leaf and Branch for every path in Obj.

Actual behavior:
Type alias 'PathIn' circularly references itself.

Related Issues: #14174, #23398

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions