microsoft / microsoft/TypeScript
Recursive conditional types are aliased
Nobody has claimed this yet.
- 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
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- 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