Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 3 additions & 21 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14089,13 +14089,6 @@ namespace ts {
}
}

function unwrapSubstitution(type: Type): Type {
if (type.flags & TypeFlags.Substitution) {
return (type as SubstitutionType).substitute;
}
return type;
}

// Transform an indexed access to a simpler form, if possible. Return the simpler form, or return
// the type itself if no transformation is possible. The writing flag indicates that the type is
// the target of an assignment.
Expand All @@ -14107,7 +14100,7 @@ namespace ts {
type[cache] = circularConstraintType;
// We recursively simplify the object type as it may in turn be an indexed access type. For example, with
// '{ [P in T]: { [Q in U]: number } }[T][U]' we want to first simplify the inner indexed access type.
const objectType = unwrapSubstitution(getSimplifiedType(type.objectType, writing));
const objectType = getSimplifiedType(type.objectType, writing);
const indexType = getSimplifiedType(type.indexType, writing);
// T[A | B] -> T[A] | T[B] (reading)
// T[A | B] -> T[A] & T[B] (writing)
Expand Down Expand Up @@ -14325,11 +14318,7 @@ namespace ts {
let combinedMapper: TypeMapper | undefined;
if (root.inferTypeParameters) {
const context = createInferenceContext(root.inferTypeParameters, /*signature*/ undefined, InferenceFlags.None);
// We skip inference of the possible `infer` types unles the `extendsType` _is_ an infer type
// if it was, it's trivial to say that extendsType = checkType, however such a pattern is used to
// "reset" the type being build up during constraint calculation and avoid making an apparently "infinite" constraint
// so in those cases we refain from performing inference and retain the uninfered type parameter
if (!checkTypeInstantiable || !some(root.inferTypeParameters, t => t === extendsType)) {
if (!checkTypeInstantiable) {
// We don't want inferences from constraints as they may cause us to eagerly resolve the
// conditional type instead of deferring resolution. Also, we always want strict function
// types rules (i.e. proper contravariance) for inferences.
Expand Down Expand Up @@ -14374,10 +14363,9 @@ namespace ts {
}
}
// Return a deferred type for a check that is neither definitely true nor definitely false
const erasedCheckType = getActualTypeVariable(checkType);
result = <ConditionalType>createType(TypeFlags.Conditional);
result.root = root;
result.checkType = erasedCheckType;
result.checkType = checkType;
result.extendsType = extendsType;
result.mapper = mapper;
result.combinedMapper = combinedMapper;
Expand Down Expand Up @@ -20210,12 +20198,6 @@ namespace ts {
invokeOnce(source, target, inferFromObjectTypes);
}
}
if (source.flags & TypeFlags.Simplifiable) {
const simplified = getSimplifiedType(source, contravariant);
if (simplified !== source) {
inferFromTypes(simplified, target);
}
}
}

function inferWithPriority(source: Type, target: Type, newPriority: InferencePriority) {
Expand Down
20 changes: 20 additions & 0 deletions tests/baselines/reference/recursiveConditionalTypes.errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -189,4 +189,24 @@ tests/cases/compiler/recursiveConditionalTypes.ts(116,9): error TS2345: Argument
!!! error TS2345: Type '[string]' is not assignable to type '[number]'.
!!! error TS2345: Type 'string' is not assignable to type 'number'.
}

// Repros from #41756

type ParseSuccess<R extends string> = { rest: R };

type ParseManyWhitespace<S extends string> =
S extends ` ${infer R0}` ?
ParseManyWhitespace<R0> extends ParseSuccess<infer R1> ? ParseSuccess<R1> : null :
ParseSuccess<S>;

type TP1 = ParseManyWhitespace<" foo">;

type ParseManyWhitespace2<S extends string> =
S extends ` ${infer R0}` ?
Helper<ParseManyWhitespace2<R0>> :
ParseSuccess<S>;

type Helper<T> = T extends ParseSuccess<infer R> ? ParseSuccess<R> : null

type TP2 = ParseManyWhitespace2<" foo">;

28 changes: 28 additions & 0 deletions tests/baselines/reference/recursiveConditionalTypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,26 @@ type Grow2<T extends unknown[], N extends number> = T['length'] extends N ? T :
function f21<T extends number>(x: Grow1<[], T>, y: Grow2<[], T>) {
f21(y, x); // Error
}

// Repros from #41756

type ParseSuccess<R extends string> = { rest: R };

type ParseManyWhitespace<S extends string> =
S extends ` ${infer R0}` ?
ParseManyWhitespace<R0> extends ParseSuccess<infer R1> ? ParseSuccess<R1> : null :
ParseSuccess<S>;

type TP1 = ParseManyWhitespace<" foo">;

type ParseManyWhitespace2<S extends string> =
S extends ` ${infer R0}` ?
Helper<ParseManyWhitespace2<R0>> :
ParseSuccess<S>;

type Helper<T> = T extends ParseSuccess<infer R> ? ParseSuccess<R> : null

type TP2 = ParseManyWhitespace2<" foo">;


//// [recursiveConditionalTypes.js]
Expand Down Expand Up @@ -214,3 +234,11 @@ declare function f20<T, U extends T>(x: Unpack1<T>, y: Unpack2<T>): void;
declare type Grow1<T extends unknown[], N extends number> = T['length'] extends N ? T : Grow1<[number, ...T], N>;
declare type Grow2<T extends unknown[], N extends number> = T['length'] extends N ? T : Grow2<[string, ...T], N>;
declare function f21<T extends number>(x: Grow1<[], T>, y: Grow2<[], T>): void;
declare type ParseSuccess<R extends string> = {
rest: R;
};
declare type ParseManyWhitespace<S extends string> = S extends ` ${infer R0}` ? ParseManyWhitespace<R0> extends ParseSuccess<infer R1> ? ParseSuccess<R1> : null : ParseSuccess<S>;
declare type TP1 = ParseManyWhitespace<" foo">;
declare type ParseManyWhitespace2<S extends string> = S extends ` ${infer R0}` ? Helper<ParseManyWhitespace2<R0>> : ParseSuccess<S>;
declare type Helper<T> = T extends ParseSuccess<infer R> ? ParseSuccess<R> : null;
declare type TP2 = ParseManyWhitespace2<" foo">;
62 changes: 62 additions & 0 deletions tests/baselines/reference/recursiveConditionalTypes.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -465,3 +465,65 @@ function f21<T extends number>(x: Grow1<[], T>, y: Grow2<[], T>) {
>x : Symbol(x, Decl(recursiveConditionalTypes.ts, 114, 31))
}

// Repros from #41756

type ParseSuccess<R extends string> = { rest: R };
>ParseSuccess : Symbol(ParseSuccess, Decl(recursiveConditionalTypes.ts, 116, 1))
>R : Symbol(R, Decl(recursiveConditionalTypes.ts, 120, 18))
>rest : Symbol(rest, Decl(recursiveConditionalTypes.ts, 120, 39))
>R : Symbol(R, Decl(recursiveConditionalTypes.ts, 120, 18))

type ParseManyWhitespace<S extends string> =
>ParseManyWhitespace : Symbol(ParseManyWhitespace, Decl(recursiveConditionalTypes.ts, 120, 50))
>S : Symbol(S, Decl(recursiveConditionalTypes.ts, 122, 25))

S extends ` ${infer R0}` ?
>S : Symbol(S, Decl(recursiveConditionalTypes.ts, 122, 25))
>R0 : Symbol(R0, Decl(recursiveConditionalTypes.ts, 123, 23))

ParseManyWhitespace<R0> extends ParseSuccess<infer R1> ? ParseSuccess<R1> : null :
>ParseManyWhitespace : Symbol(ParseManyWhitespace, Decl(recursiveConditionalTypes.ts, 120, 50))
>R0 : Symbol(R0, Decl(recursiveConditionalTypes.ts, 123, 23))
>ParseSuccess : Symbol(ParseSuccess, Decl(recursiveConditionalTypes.ts, 116, 1))
>R1 : Symbol(R1, Decl(recursiveConditionalTypes.ts, 124, 58))
>ParseSuccess : Symbol(ParseSuccess, Decl(recursiveConditionalTypes.ts, 116, 1))
>R1 : Symbol(R1, Decl(recursiveConditionalTypes.ts, 124, 58))

ParseSuccess<S>;
>ParseSuccess : Symbol(ParseSuccess, Decl(recursiveConditionalTypes.ts, 116, 1))
>S : Symbol(S, Decl(recursiveConditionalTypes.ts, 122, 25))

type TP1 = ParseManyWhitespace<" foo">;
>TP1 : Symbol(TP1, Decl(recursiveConditionalTypes.ts, 125, 24))
>ParseManyWhitespace : Symbol(ParseManyWhitespace, Decl(recursiveConditionalTypes.ts, 120, 50))

type ParseManyWhitespace2<S extends string> =
>ParseManyWhitespace2 : Symbol(ParseManyWhitespace2, Decl(recursiveConditionalTypes.ts, 127, 39))
>S : Symbol(S, Decl(recursiveConditionalTypes.ts, 129, 26))

S extends ` ${infer R0}` ?
>S : Symbol(S, Decl(recursiveConditionalTypes.ts, 129, 26))
>R0 : Symbol(R0, Decl(recursiveConditionalTypes.ts, 130, 23))

Helper<ParseManyWhitespace2<R0>> :
>Helper : Symbol(Helper, Decl(recursiveConditionalTypes.ts, 132, 24))
>ParseManyWhitespace2 : Symbol(ParseManyWhitespace2, Decl(recursiveConditionalTypes.ts, 127, 39))
>R0 : Symbol(R0, Decl(recursiveConditionalTypes.ts, 130, 23))

ParseSuccess<S>;
>ParseSuccess : Symbol(ParseSuccess, Decl(recursiveConditionalTypes.ts, 116, 1))
>S : Symbol(S, Decl(recursiveConditionalTypes.ts, 129, 26))

type Helper<T> = T extends ParseSuccess<infer R> ? ParseSuccess<R> : null
>Helper : Symbol(Helper, Decl(recursiveConditionalTypes.ts, 132, 24))
>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 134, 12))
>T : Symbol(T, Decl(recursiveConditionalTypes.ts, 134, 12))
>ParseSuccess : Symbol(ParseSuccess, Decl(recursiveConditionalTypes.ts, 116, 1))
>R : Symbol(R, Decl(recursiveConditionalTypes.ts, 134, 45))
>ParseSuccess : Symbol(ParseSuccess, Decl(recursiveConditionalTypes.ts, 116, 1))
>R : Symbol(R, Decl(recursiveConditionalTypes.ts, 134, 45))

type TP2 = ParseManyWhitespace2<" foo">;
>TP2 : Symbol(TP2, Decl(recursiveConditionalTypes.ts, 134, 73))
>ParseManyWhitespace2 : Symbol(ParseManyWhitespace2, Decl(recursiveConditionalTypes.ts, 127, 39))

32 changes: 32 additions & 0 deletions tests/baselines/reference/recursiveConditionalTypes.types
Original file line number Diff line number Diff line change
Expand Up @@ -310,3 +310,35 @@ function f21<T extends number>(x: Grow1<[], T>, y: Grow2<[], T>) {
>x : Grow1<[], T>
}

// Repros from #41756

type ParseSuccess<R extends string> = { rest: R };
>ParseSuccess : ParseSuccess<R>
>rest : R

type ParseManyWhitespace<S extends string> =
>ParseManyWhitespace : ParseManyWhitespace<S>

S extends ` ${infer R0}` ?
ParseManyWhitespace<R0> extends ParseSuccess<infer R1> ? ParseSuccess<R1> : null :
>null : null

ParseSuccess<S>;

type TP1 = ParseManyWhitespace<" foo">;
>TP1 : ParseSuccess<"foo">

type ParseManyWhitespace2<S extends string> =
>ParseManyWhitespace2 : ParseManyWhitespace2<S>

S extends ` ${infer R0}` ?
Helper<ParseManyWhitespace2<R0>> :
ParseSuccess<S>;

type Helper<T> = T extends ParseSuccess<infer R> ? ParseSuccess<R> : null
>Helper : Helper<T>
>null : null

type TP2 = ParseManyWhitespace2<" foo">;
>TP2 : ParseSuccess<"foo">

20 changes: 20 additions & 0 deletions tests/cases/compiler/recursiveConditionalTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,3 +119,23 @@ type Grow2<T extends unknown[], N extends number> = T['length'] extends N ? T :
function f21<T extends number>(x: Grow1<[], T>, y: Grow2<[], T>) {
f21(y, x); // Error
}

// Repros from #41756

type ParseSuccess<R extends string> = { rest: R };

type ParseManyWhitespace<S extends string> =
S extends ` ${infer R0}` ?
ParseManyWhitespace<R0> extends ParseSuccess<infer R1> ? ParseSuccess<R1> : null :
ParseSuccess<S>;

type TP1 = ParseManyWhitespace<" foo">;

type ParseManyWhitespace2<S extends string> =
S extends ` ${infer R0}` ?
Helper<ParseManyWhitespace2<R0>> :
ParseSuccess<S>;

type Helper<T> = T extends ParseSuccess<infer R> ? ParseSuccess<R> : null

type TP2 = ParseManyWhitespace2<" foo">;