diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 54d21cf16f2cb..3b3d4256fecfc 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -39347,20 +39347,17 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } } - function getNonGenericReturnTypeOfSingleCallSignature(funcType: Type) { + function getReturnTypeOfSingleNonGenericCallSignature(funcType: Type) { const signature = getSingleCallSignature(funcType); - if (signature) { - const returnType = getReturnTypeOfSignature(signature); - if (!signature.typeParameters || !couldContainTypeVariables(returnType)) { - return returnType; - } + if (signature && !signature.typeParameters) { + return getReturnTypeOfSignature(signature); } } function getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr: CallChain) { const funcType = checkExpression(expr.expression); const nonOptionalType = getOptionalExpressionType(funcType, expr.expression); - const returnType = getNonGenericReturnTypeOfSingleCallSignature(funcType); + const returnType = getReturnTypeOfSingleNonGenericCallSignature(funcType); return returnType && propagateOptionalTypeMarker(returnType, expr, nonOptionalType !== funcType); } @@ -39409,7 +39406,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // signature where we can just fetch the return type without checking the arguments. if (isCallExpression(expr) && expr.expression.kind !== SyntaxKind.SuperKeyword && !isRequireCall(expr, /*requireStringLiteralLikeArgument*/ true) && !isSymbolOrSymbolForCall(expr)) { return isCallChain(expr) ? getReturnTypeOfSingleNonGenericSignatureOfCallChain(expr) : - getNonGenericReturnTypeOfSingleCallSignature(checkNonNullExpression(expr.expression)); + getReturnTypeOfSingleNonGenericCallSignature(checkNonNullExpression(expr.expression)); } else if (isAssertionExpression(expr) && !isConstTypeReference(expr.type)) { return getTypeFromTypeNode((expr as TypeAssertion).type); diff --git a/tests/baselines/reference/arrayFrom.types b/tests/baselines/reference/arrayFrom.types index b3189e4ef57a3..dda3f91d2698f 100644 --- a/tests/baselines/reference/arrayFrom.types +++ b/tests/baselines/reference/arrayFrom.types @@ -31,7 +31,7 @@ const inputALike: ArrayLike = { length: 0 }; const inputARand = getEither(inputA, inputALike); >inputARand : ArrayLike | Iterable >getEither(inputA, inputALike) : ArrayLike | Iterable ->getEither : (in1: Iterable, in2: ArrayLike) => Iterable | ArrayLike +>getEither : (in1: Iterable, in2: ArrayLike) => ArrayLike | Iterable >inputA : A[] >inputALike : ArrayLike @@ -163,12 +163,12 @@ const result11: B[] = Array.from(inputASet, ({ a }): B => ({ b: a })); // the ?: as always taking the false branch, narrowing to ArrayLike, // even when the type is written as : Iterable|ArrayLike function getEither (in1: Iterable, in2: ArrayLike) { ->getEither : (in1: Iterable, in2: ArrayLike) => Iterable | ArrayLike +>getEither : (in1: Iterable, in2: ArrayLike) => ArrayLike | Iterable >in1 : Iterable >in2 : ArrayLike return Math.random() > 0.5 ? in1 : in2; ->Math.random() > 0.5 ? in1 : in2 : Iterable | ArrayLike +>Math.random() > 0.5 ? in1 : in2 : ArrayLike | Iterable >Math.random() > 0.5 : boolean >Math.random() : number >Math.random : () => number diff --git a/tests/baselines/reference/circularReferenceInReturnType.errors.txt b/tests/baselines/reference/circularReferenceInReturnType.errors.txt new file mode 100644 index 0000000000000..d90cf8523f5a4 --- /dev/null +++ b/tests/baselines/reference/circularReferenceInReturnType.errors.txt @@ -0,0 +1,19 @@ +circularReferenceInReturnType.ts(3,7): error TS7022: 'res1' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. +circularReferenceInReturnType.ts(9,7): error TS7022: 'res3' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. + + +==== circularReferenceInReturnType.ts (2 errors) ==== + // inference fails for res1 and res2, but ideally should not + declare function fn1(cb: () => T): string; + const res1 = fn1(() => res1); + ~~~~ +!!! error TS7022: 'res1' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. + + declare function fn2(): (cb: () => any) => (a: T) => void; + const res2 = fn2()(() => res2); + + declare function fn3(): (cb: (arg: T2) => any) => (a: T) => void; + const res3 = fn3()(() => res3); + ~~~~ +!!! error TS7022: 'res3' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. + \ No newline at end of file diff --git a/tests/baselines/reference/circularReferenceInReturnType.symbols b/tests/baselines/reference/circularReferenceInReturnType.symbols index 7baad0aa81c5e..d126adc332884 100644 --- a/tests/baselines/reference/circularReferenceInReturnType.symbols +++ b/tests/baselines/reference/circularReferenceInReturnType.symbols @@ -1,41 +1,42 @@ //// [tests/cases/compiler/circularReferenceInReturnType.ts] //// === circularReferenceInReturnType.ts === +// inference fails for res1 and res2, but ideally should not declare function fn1(cb: () => T): string; >fn1 : Symbol(fn1, Decl(circularReferenceInReturnType.ts, 0, 0)) ->T : Symbol(T, Decl(circularReferenceInReturnType.ts, 0, 21)) ->cb : Symbol(cb, Decl(circularReferenceInReturnType.ts, 0, 24)) ->T : Symbol(T, Decl(circularReferenceInReturnType.ts, 0, 21)) +>T : Symbol(T, Decl(circularReferenceInReturnType.ts, 1, 21)) +>cb : Symbol(cb, Decl(circularReferenceInReturnType.ts, 1, 24)) +>T : Symbol(T, Decl(circularReferenceInReturnType.ts, 1, 21)) const res1 = fn1(() => res1); ->res1 : Symbol(res1, Decl(circularReferenceInReturnType.ts, 1, 5)) +>res1 : Symbol(res1, Decl(circularReferenceInReturnType.ts, 2, 5)) >fn1 : Symbol(fn1, Decl(circularReferenceInReturnType.ts, 0, 0)) ->res1 : Symbol(res1, Decl(circularReferenceInReturnType.ts, 1, 5)) +>res1 : Symbol(res1, Decl(circularReferenceInReturnType.ts, 2, 5)) declare function fn2(): (cb: () => any) => (a: T) => void; ->fn2 : Symbol(fn2, Decl(circularReferenceInReturnType.ts, 1, 29)) ->T : Symbol(T, Decl(circularReferenceInReturnType.ts, 3, 21)) ->cb : Symbol(cb, Decl(circularReferenceInReturnType.ts, 3, 28)) ->a : Symbol(a, Decl(circularReferenceInReturnType.ts, 3, 47)) ->T : Symbol(T, Decl(circularReferenceInReturnType.ts, 3, 21)) +>fn2 : Symbol(fn2, Decl(circularReferenceInReturnType.ts, 2, 29)) +>T : Symbol(T, Decl(circularReferenceInReturnType.ts, 4, 21)) +>cb : Symbol(cb, Decl(circularReferenceInReturnType.ts, 4, 28)) +>a : Symbol(a, Decl(circularReferenceInReturnType.ts, 4, 47)) +>T : Symbol(T, Decl(circularReferenceInReturnType.ts, 4, 21)) const res2 = fn2()(() => res2); ->res2 : Symbol(res2, Decl(circularReferenceInReturnType.ts, 4, 5)) ->fn2 : Symbol(fn2, Decl(circularReferenceInReturnType.ts, 1, 29)) ->res2 : Symbol(res2, Decl(circularReferenceInReturnType.ts, 4, 5)) +>res2 : Symbol(res2, Decl(circularReferenceInReturnType.ts, 5, 5)) +>fn2 : Symbol(fn2, Decl(circularReferenceInReturnType.ts, 2, 29)) +>res2 : Symbol(res2, Decl(circularReferenceInReturnType.ts, 5, 5)) declare function fn3(): (cb: (arg: T2) => any) => (a: T) => void; ->fn3 : Symbol(fn3, Decl(circularReferenceInReturnType.ts, 4, 31)) ->T : Symbol(T, Decl(circularReferenceInReturnType.ts, 6, 21)) ->T2 : Symbol(T2, Decl(circularReferenceInReturnType.ts, 6, 28)) ->cb : Symbol(cb, Decl(circularReferenceInReturnType.ts, 6, 32)) ->arg : Symbol(arg, Decl(circularReferenceInReturnType.ts, 6, 37)) ->T2 : Symbol(T2, Decl(circularReferenceInReturnType.ts, 6, 28)) ->a : Symbol(a, Decl(circularReferenceInReturnType.ts, 6, 58)) ->T : Symbol(T, Decl(circularReferenceInReturnType.ts, 6, 21)) +>fn3 : Symbol(fn3, Decl(circularReferenceInReturnType.ts, 5, 31)) +>T : Symbol(T, Decl(circularReferenceInReturnType.ts, 7, 21)) +>T2 : Symbol(T2, Decl(circularReferenceInReturnType.ts, 7, 28)) +>cb : Symbol(cb, Decl(circularReferenceInReturnType.ts, 7, 32)) +>arg : Symbol(arg, Decl(circularReferenceInReturnType.ts, 7, 37)) +>T2 : Symbol(T2, Decl(circularReferenceInReturnType.ts, 7, 28)) +>a : Symbol(a, Decl(circularReferenceInReturnType.ts, 7, 58)) +>T : Symbol(T, Decl(circularReferenceInReturnType.ts, 7, 21)) const res3 = fn3()(() => res3); ->res3 : Symbol(res3, Decl(circularReferenceInReturnType.ts, 7, 5)) ->fn3 : Symbol(fn3, Decl(circularReferenceInReturnType.ts, 4, 31)) ->res3 : Symbol(res3, Decl(circularReferenceInReturnType.ts, 7, 5)) +>res3 : Symbol(res3, Decl(circularReferenceInReturnType.ts, 8, 5)) +>fn3 : Symbol(fn3, Decl(circularReferenceInReturnType.ts, 5, 31)) +>res3 : Symbol(res3, Decl(circularReferenceInReturnType.ts, 8, 5)) diff --git a/tests/baselines/reference/circularReferenceInReturnType.types b/tests/baselines/reference/circularReferenceInReturnType.types index 68fb3d2bd653f..a1c4fd9e0a1ab 100644 --- a/tests/baselines/reference/circularReferenceInReturnType.types +++ b/tests/baselines/reference/circularReferenceInReturnType.types @@ -1,16 +1,17 @@ //// [tests/cases/compiler/circularReferenceInReturnType.ts] //// === circularReferenceInReturnType.ts === +// inference fails for res1 and res2, but ideally should not declare function fn1(cb: () => T): string; >fn1 : (cb: () => T) => string >cb : () => T const res1 = fn1(() => res1); ->res1 : string +>res1 : any >fn1(() => res1) : string >fn1 : (cb: () => T) => string ->() => res1 : () => string ->res1 : string +>() => res1 : () => any +>res1 : any declare function fn2(): (cb: () => any) => (a: T) => void; >fn2 : () => (cb: () => any) => (a: T) => void @@ -32,10 +33,10 @@ declare function fn3(): (cb: (arg: T2) => any) => (a: T) => void; >a : T const res3 = fn3()(() => res3); ->res3 : (a: unknown) => void +>res3 : any >fn3()(() => res3) : (a: unknown) => void >fn3() : (cb: (arg: T2) => any) => (a: unknown) => void >fn3 : () => (cb: (arg: T2) => any) => (a: T) => void ->() => res3 : () => (a: unknown) => void ->res3 : (a: unknown) => void +>() => res3 : () => any +>res3 : any diff --git a/tests/baselines/reference/circularReferenceInReturnType2.errors.txt b/tests/baselines/reference/circularReferenceInReturnType2.errors.txt new file mode 100644 index 0000000000000..4f075e69a6814 --- /dev/null +++ b/tests/baselines/reference/circularReferenceInReturnType2.errors.txt @@ -0,0 +1,58 @@ +circularReferenceInReturnType2.ts(39,7): error TS7022: 'A' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. + + +==== circularReferenceInReturnType2.ts (1 errors) ==== + type ObjectType = { + kind: "object"; + __source: (source: Source) => void; + }; + + type Field = { + __key: (key: Key) => void; + __source: (source: Source) => void; + }; + + declare const object: () => < + Fields extends { + [Key in keyof Fields]: Field; + } + >(config: { + name: string; + fields: Fields | (() => Fields); + }) => ObjectType; + + type InferValueFromObjectType> = + Type extends ObjectType ? Source : never; + + type FieldResolver> = ( + source: Source + ) => InferValueFromObjectType; + + type FieldFuncArgs> = { + type: Type; + resolve: FieldResolver; + }; + + declare const field: , Key extends string>( + field: FieldFuncArgs + ) => Field; + + type Something = { foo: number }; + + // inference fails here, but ideally should not + const A = object()({ + ~ +!!! error TS7022: 'A' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. + name: "A", + fields: () => ({ + a: field({ + type: A, + resolve() { + return { + foo: 100, + }; + }, + }), + }), + }); + \ No newline at end of file diff --git a/tests/baselines/reference/circularReferenceInReturnType2.symbols b/tests/baselines/reference/circularReferenceInReturnType2.symbols index 119e9245ea0e2..beb82053ac663 100644 --- a/tests/baselines/reference/circularReferenceInReturnType2.symbols +++ b/tests/baselines/reference/circularReferenceInReturnType2.symbols @@ -126,31 +126,32 @@ type Something = { foo: number }; >Something : Symbol(Something, Decl(circularReferenceInReturnType2.ts, 33, 24)) >foo : Symbol(foo, Decl(circularReferenceInReturnType2.ts, 35, 18)) +// inference fails here, but ideally should not const A = object()({ ->A : Symbol(A, Decl(circularReferenceInReturnType2.ts, 37, 5)) +>A : Symbol(A, Decl(circularReferenceInReturnType2.ts, 38, 5)) >object : Symbol(object, Decl(circularReferenceInReturnType2.ts, 10, 13)) >Something : Symbol(Something, Decl(circularReferenceInReturnType2.ts, 33, 24)) name: "A", ->name : Symbol(name, Decl(circularReferenceInReturnType2.ts, 37, 31)) +>name : Symbol(name, Decl(circularReferenceInReturnType2.ts, 38, 31)) fields: () => ({ ->fields : Symbol(fields, Decl(circularReferenceInReturnType2.ts, 38, 12)) +>fields : Symbol(fields, Decl(circularReferenceInReturnType2.ts, 39, 12)) a: field({ ->a : Symbol(a, Decl(circularReferenceInReturnType2.ts, 39, 18)) +>a : Symbol(a, Decl(circularReferenceInReturnType2.ts, 40, 18)) >field : Symbol(field, Decl(circularReferenceInReturnType2.ts, 31, 13)) type: A, ->type : Symbol(type, Decl(circularReferenceInReturnType2.ts, 40, 14)) ->A : Symbol(A, Decl(circularReferenceInReturnType2.ts, 37, 5)) +>type : Symbol(type, Decl(circularReferenceInReturnType2.ts, 41, 14)) +>A : Symbol(A, Decl(circularReferenceInReturnType2.ts, 38, 5)) resolve() { ->resolve : Symbol(resolve, Decl(circularReferenceInReturnType2.ts, 41, 14)) +>resolve : Symbol(resolve, Decl(circularReferenceInReturnType2.ts, 42, 14)) return { foo: 100, ->foo : Symbol(foo, Decl(circularReferenceInReturnType2.ts, 43, 16)) +>foo : Symbol(foo, Decl(circularReferenceInReturnType2.ts, 44, 16)) }; }, diff --git a/tests/baselines/reference/circularReferenceInReturnType2.types b/tests/baselines/reference/circularReferenceInReturnType2.types index 49096d1921ff4..de202f7c8cad9 100644 --- a/tests/baselines/reference/circularReferenceInReturnType2.types +++ b/tests/baselines/reference/circularReferenceInReturnType2.types @@ -79,8 +79,9 @@ type Something = { foo: number }; >Something : { foo: number; } >foo : number +// inference fails here, but ideally should not const A = object()({ ->A : ObjectType +>A : any >object()({ name: "A", fields: () => ({ a: field({ type: A, resolve() { return { foo: 100, }; }, }), }),}) : ObjectType >object() : ; }>(config: { name: string; fields: Fields | (() => Fields); }) => ObjectType >object : () => ; }>(config: { name: string; fields: Fields | (() => Fields); }) => ObjectType @@ -100,11 +101,11 @@ const A = object()({ >a : Field >field({ type: A, resolve() { return { foo: 100, }; }, }) : Field >field : , Key extends string>(field: FieldFuncArgs) => Field ->{ type: A, resolve() { return { foo: 100, }; }, } : { type: ObjectType; resolve(): { foo: number; }; } +>{ type: A, resolve() { return { foo: 100, }; }, } : { type: any; resolve(): { foo: number; }; } type: A, ->type : ObjectType ->A : ObjectType +>type : any +>A : any resolve() { >resolve : () => { foo: number; } diff --git a/tests/cases/compiler/circularReferenceInReturnType.ts b/tests/cases/compiler/circularReferenceInReturnType.ts index 05ed1a56f509c..50f82f9e37156 100644 --- a/tests/cases/compiler/circularReferenceInReturnType.ts +++ b/tests/cases/compiler/circularReferenceInReturnType.ts @@ -1,6 +1,7 @@ // @strict: true // @noEmit: true +// inference fails for res1 and res2, but ideally should not declare function fn1(cb: () => T): string; const res1 = fn1(() => res1); diff --git a/tests/cases/compiler/circularReferenceInReturnType2.ts b/tests/cases/compiler/circularReferenceInReturnType2.ts index f9fa8fef33c7e..3fb431ffc44a4 100644 --- a/tests/cases/compiler/circularReferenceInReturnType2.ts +++ b/tests/cases/compiler/circularReferenceInReturnType2.ts @@ -38,6 +38,7 @@ declare const field: , Key extends string>( type Something = { foo: number }; +// inference fails here, but ideally should not const A = object()({ name: "A", fields: () => ({