microsoft / microsoft/TypeScript

Recursive conditional types are aliased

Open
#22,575 6 comments 2 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Experience Enhancement Suggestion
Dominant language
Go
Stars
111k
Forks
14.3k
Avg merge
2d 4h
Merged PRs (30d)
132

Description

I am trying to create a conditional type that converts

type Before = {
  a: string;
  b: number;
  c: string | undefined;
  d: number | undefined;
  nested: {
    a2: string;
    b2: number;
    c2: string | undefined;
    d2: number | undefined;
    nested2: {
      a3: string;
      b3: number;
      c3: string | undefined;
      d3: number | undefined;
    };
  };
};

to

type After = {
  a: string;
  b: number;
  c?: string | undefined;
  d?: number | undefined;
  nested: {
    a2: string;
    b2: number;
    c2?: string | undefined;
    d2?: number | undefined;
    nested2: {
      a3: string;
      b3: number;
      c3?: string | undefined;
      d3?: number | undefined;
    };
  };
};

When I hover on After in the below code


const fnBefore = (input: Before) => {
  return input;
};

const fnAfter = (input: After) => {
  return input;
};

it shows

type After = {
    c?: string | undefined;
    d?: number | undefined;
    a: string;
    b: number;
    nested: Flatten<{
        c2?: string | undefined;
        d2?: number | undefined;
    } & {
        a2: string;
        b2: number;
        nested2: Flatten<{
            c3?: string | undefined;
            d3?: number | undefined;
        } & RequiredProps>;
    }>;
}

instead of properly converted type.

According to #22011

If an conditional type is instantiated over 100 times, we consider that to be too deep.
At that point, we try to find the respective alias type that contains that conditional type.

only more complex types should be aliased, but I always face this issue.
Also for simple types:

type Simple = {
  nested: {
    a2: string;
    c2: string | undefined;
  };
};

TypeScript Version: 2.8.0-dev.201180314

Full Code

type Before = {
  a: string;
  b: number;
  c: string | undefined;
  d: number | undefined;
  nested: {
    a2: string;
    b2: number;
    c2: string | undefined;
    d2: number | undefined;
    nested2: {
      a3: string;
      b3: number;
      c3: string | undefined;
      d3: number | undefined;
    };
  };
};

type Simple = {
  nested: {
    a2: string;
    c2: string | undefined;
  };
};

type Flatten<T> = { [K in keyof T]: T[K] };

type OptionalPropNames<T> = { [P in keyof T]: undefined extends T[P] ? P : never }[keyof T];
type RequiredPropNames<T> = { [P in keyof T]: undefined extends T[P] ? never : P }[keyof T];

type OptionalProps<T> = { [P in OptionalPropNames<T>]: T[P] };
type RequiredProps<T> = { [P in RequiredPropNames<T>]: T[P] };

type MakeOptional<T> = { [P in keyof T]?: T[P] };

type ConvertObject<T> = Flatten<MakeOptional<OptionalProps<T>> & RequiredProps<T>>;

type DeepConvertObject<T> = ConvertObject<{ [P in keyof T]: DeepConvert<T[P]> }>;

type DeepConvert<T> = T extends object ? DeepConvertObject<T> : T;

type After = DeepConvert<Before>;
type SimpleAfter = DeepConvert<Simple>;

const fnBefore = (input: Before) => {
  return input;
};

const fnAfter = (input: After) => {
  return input;
};

Expected behavior:
The tooltip should be shown without aliases.

Actual behavior:
The tooltip is shown with aliases.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Reproduce the provided TypeScript 2.8 example and inspect the hover output for After and SimpleAfter. Start with the conditional-type instantiation and type-alias display behavior described in the issue; done means the tooltip expands these recursive conditional types without the unwanted aliases.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
compilers
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Clearly specified
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.