Skip to content

Commit

Permalink
Emit extends clause for synthetic infer typesduring declaration emit
Browse files Browse the repository at this point in the history
  • Loading branch information
rbuckton committed Mar 24, 2022
1 parent 0aded71 commit 01b9a2d
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 6 deletions.
19 changes: 16 additions & 3 deletions src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4977,7 +4977,20 @@ namespace ts {
if (type.flags & TypeFlags.TypeParameter || objectFlags & ObjectFlags.ClassOrInterface) {
if (type.flags & TypeFlags.TypeParameter && contains(context.inferTypeParameters, type)) {
context.approximateLength += (symbolName(type.symbol).length + 6);
return factory.createInferTypeNode(typeParameterToDeclarationWithConstraint(type as TypeParameter, context, /*constraintNode*/ undefined));
let constraintNode: TypeNode | undefined;
const constraint = getConstraintOfTypeParameter(type as TypeParameter);
if (constraint) {
// If the infer type has a constraint that is not the same as the constraint
// we would have normally inferred based on context, we emit the constraint
// using `infer T extends ?`. We omit inferred constraints from type references
// as they may be elided.
const inferredConstraint = getInferredTypeParameterConstraint(type as TypeParameter, /*omitTypeReferences*/ true);
if (!(inferredConstraint && isTypeIdenticalTo(constraint, inferredConstraint))) {
context.approximateLength += 9;
constraintNode = constraint && typeToTypeNodeHelper(constraint, context);
}
}
return factory.createInferTypeNode(typeParameterToDeclarationWithConstraint(type as TypeParameter, context, constraintNode));
}
if (context.flags & NodeBuilderFlags.GenerateNamesForShadowedTypeParams &&
type.flags & TypeFlags.TypeParameter &&
Expand Down Expand Up @@ -13259,7 +13272,7 @@ namespace ts {
return mapDefined(filter(type.symbol && type.symbol.declarations, isTypeParameterDeclaration), getEffectiveConstraintOfTypeParameter)[0];
}

function getInferredTypeParameterConstraint(typeParameter: TypeParameter) {
function getInferredTypeParameterConstraint(typeParameter: TypeParameter, omitTypeReferences?: boolean) {
let inferences: Type[] | undefined;
if (typeParameter.symbol?.declarations) {
for (const declaration of typeParameter.symbol.declarations) {
Expand All @@ -13269,7 +13282,7 @@ namespace ts {
// corresponding type parameter in 'Foo'. When multiple 'infer T' declarations are
// present, we form an intersection of the inferred constraint types.
const [childTypeParameter = declaration.parent, grandParent] = walkUpParenthesizedTypesAndGetParentAndChild(declaration.parent.parent);
if (grandParent.kind === SyntaxKind.TypeReference) {
if (grandParent.kind === SyntaxKind.TypeReference && !omitTypeReferences) {
const typeReference = grandParent as TypeReferenceNode;
const typeParameters = getTypeParametersForTypeReference(typeReference);
if (typeParameters) {
Expand Down
23 changes: 22 additions & 1 deletion tests/baselines/reference/inferTypesWithExtends1.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,10 +123,26 @@ type X21_T5 = X21<1 | 2, 3>; // never

// from mongoose
type IfEquals<X, Y, A, B> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? A : B;


declare const x1: <T>() => (T extends infer U extends number ? 1 : 0);
function f1() {
return x1;
}

type ExpectNumber<T extends number> = T;
declare const x2: <T>() => (T extends ExpectNumber<infer U> ? 1 : 0);
function f2() {
return x2;
}

//// [inferTypesWithExtends1.js]
"use strict";
function f1() {
return x1;
}
function f2() {
return x2;
}


//// [inferTypesWithExtends1.d.ts]
Expand Down Expand Up @@ -275,3 +291,8 @@ declare type X21_T3 = X21<1 | 2, 1>;
declare type X21_T4 = X21<1 | 2, 2 | 3>;
declare type X21_T5 = X21<1 | 2, 3>;
declare type IfEquals<X, Y, A, B> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? A : B;
declare const x1: <T>() => (T extends infer U extends number ? 1 : 0);
declare function f1(): <T>() => T extends infer U extends number ? 1 : 0;
declare type ExpectNumber<T extends number> = T;
declare const x2: <T>() => (T extends ExpectNumber<infer U> ? 1 : 0);
declare function f2(): <T>() => T extends infer U extends number ? 1 : 0;
31 changes: 31 additions & 0 deletions tests/baselines/reference/inferTypesWithExtends1.symbols
Original file line number Diff line number Diff line change
Expand Up @@ -498,3 +498,34 @@ type IfEquals<X, Y, A, B> = (<T>() => T extends X ? 1 : 2) extends <T>() => T ex
>A : Symbol(A, Decl(inferTypesWithExtends1.ts, 123, 19))
>B : Symbol(B, Decl(inferTypesWithExtends1.ts, 123, 22))

declare const x1: <T>() => (T extends infer U extends number ? 1 : 0);
>x1 : Symbol(x1, Decl(inferTypesWithExtends1.ts, 125, 13))
>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 125, 19))
>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 125, 19))
>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 125, 43))

function f1() {
>f1 : Symbol(f1, Decl(inferTypesWithExtends1.ts, 125, 70))

return x1;
>x1 : Symbol(x1, Decl(inferTypesWithExtends1.ts, 125, 13))
}

type ExpectNumber<T extends number> = T;
>ExpectNumber : Symbol(ExpectNumber, Decl(inferTypesWithExtends1.ts, 128, 1))
>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 130, 18))
>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 130, 18))

declare const x2: <T>() => (T extends ExpectNumber<infer U> ? 1 : 0);
>x2 : Symbol(x2, Decl(inferTypesWithExtends1.ts, 131, 13))
>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 131, 19))
>T : Symbol(T, Decl(inferTypesWithExtends1.ts, 131, 19))
>ExpectNumber : Symbol(ExpectNumber, Decl(inferTypesWithExtends1.ts, 128, 1))
>U : Symbol(U, Decl(inferTypesWithExtends1.ts, 131, 56))

function f2() {
>f2 : Symbol(f2, Decl(inferTypesWithExtends1.ts, 131, 69))

return x2;
>x2 : Symbol(x2, Decl(inferTypesWithExtends1.ts, 131, 13))
}
22 changes: 22 additions & 0 deletions tests/baselines/reference/inferTypesWithExtends1.types
Original file line number Diff line number Diff line change
Expand Up @@ -307,3 +307,25 @@ type X21_T5 = X21<1 | 2, 3>; // never
type IfEquals<X, Y, A, B> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? A : B;
>IfEquals : IfEquals<X, Y, A, B>

declare const x1: <T>() => (T extends infer U extends number ? 1 : 0);
>x1 : <T>() => T extends infer U extends number ? 1 : 0

function f1() {
>f1 : () => <T>() => T extends infer U extends number ? 1 : 0

return x1;
>x1 : <T>() => T extends infer U extends number ? 1 : 0
}

type ExpectNumber<T extends number> = T;
>ExpectNumber : T

declare const x2: <T>() => (T extends ExpectNumber<infer U> ? 1 : 0);
>x2 : <T>() => T extends infer U extends number ? 1 : 0

function f2() {
>f2 : () => <T>() => T extends infer U extends number ? 1 : 0

return x2;
>x2 : <T>() => T extends infer U extends number ? 1 : 0
}
4 changes: 2 additions & 2 deletions tests/baselines/reference/infiniteConstraints.types
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ type AProp<T extends { a: string }> = T
>a : string

declare function myBug<
>myBug : <T extends { [K in keyof T]: T[K] extends infer U ? U : never; }>(arg: T) => T
>myBug : <T extends { [K in keyof T]: T[K] extends infer U extends { a: string; } ? U : never; }>(arg: T) => T

T extends { [K in keyof T]: T[K] extends AProp<infer U> ? U : never }
>(arg: T): T
Expand All @@ -24,7 +24,7 @@ declare function myBug<
const out = myBug({obj1: {a: "test"}})
>out : { obj1: { a: string; }; }
>myBug({obj1: {a: "test"}}) : { obj1: { a: string; }; }
>myBug : <T extends { [K in keyof T]: T[K] extends infer U ? U : never; }>(arg: T) => T
>myBug : <T extends { [K in keyof T]: T[K] extends infer U extends { a: string; } ? U : never; }>(arg: T) => T
>{obj1: {a: "test"}} : { obj1: { a: string; }; }
>obj1 : { a: string; }
>{a: "test"} : { a: string; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -125,3 +125,14 @@ type X21_T5 = X21<1 | 2, 3>; // never

// from mongoose
type IfEquals<X, Y, A, B> = (<T>() => T extends X ? 1 : 2) extends <T>() => T extends Y ? 1 : 2 ? A : B;

declare const x1: <T>() => (T extends infer U extends number ? 1 : 0);
function f1() {
return x1;
}

type ExpectNumber<T extends number> = T;
declare const x2: <T>() => (T extends ExpectNumber<infer U> ? 1 : 0);
function f2() {
return x2;
}

0 comments on commit 01b9a2d

Please sign in to comment.