From 6c1d25e4341aa9da87b120fdfabcd4c306113688 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Fri, 3 Mar 2023 13:35:49 -0800 Subject: [PATCH 01/11] add repro test --- .../classVarianceCircularity.errors.txt | 20 +++++++++ .../reference/classVarianceCircularity.js | 32 +++++++++++++ .../classVarianceCircularity.symbols | 42 +++++++++++++++++ .../reference/classVarianceCircularity.types | 45 +++++++++++++++++++ .../compiler/classVarianceCircularity.ts | 16 +++++++ 5 files changed, 155 insertions(+) create mode 100644 tests/baselines/reference/classVarianceCircularity.errors.txt create mode 100644 tests/baselines/reference/classVarianceCircularity.js create mode 100644 tests/baselines/reference/classVarianceCircularity.symbols create mode 100644 tests/baselines/reference/classVarianceCircularity.types create mode 100644 tests/cases/compiler/classVarianceCircularity.ts diff --git a/tests/baselines/reference/classVarianceCircularity.errors.txt b/tests/baselines/reference/classVarianceCircularity.errors.txt new file mode 100644 index 0000000000000..6d76e0a12af97 --- /dev/null +++ b/tests/baselines/reference/classVarianceCircularity.errors.txt @@ -0,0 +1,20 @@ +tests/cases/compiler/classVarianceCircularity.ts(13,5): error TS7022: 'Value' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. + + +==== tests/cases/compiler/classVarianceCircularity.ts (1 errors) ==== + // Issue #52813 + + function f() { + const b = new Bar(); + // Uncomment to create error + console.log(b.Value); + } + + class Bar { + num!: number; + // Or swap these two lines + Field: number = (this as Bar).num; + Value = (this as Bar).num; + ~~~~~ +!!! error TS7022: 'Value' 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/classVarianceCircularity.js b/tests/baselines/reference/classVarianceCircularity.js new file mode 100644 index 0000000000000..60b4dd2995aab --- /dev/null +++ b/tests/baselines/reference/classVarianceCircularity.js @@ -0,0 +1,32 @@ +//// [classVarianceCircularity.ts] +// Issue #52813 + +function f() { + const b = new Bar(); + // Uncomment to create error + console.log(b.Value); +} + +class Bar { + num!: number; + // Or swap these two lines + Field: number = (this as Bar).num; + Value = (this as Bar).num; +} + +//// [classVarianceCircularity.js] +"use strict"; +// Issue #52813 +function f() { + var b = new Bar(); + // Uncomment to create error + console.log(b.Value); +} +var Bar = /** @class */ (function () { + function Bar() { + // Or swap these two lines + this.Field = this.num; + this.Value = this.num; + } + return Bar; +}()); diff --git a/tests/baselines/reference/classVarianceCircularity.symbols b/tests/baselines/reference/classVarianceCircularity.symbols new file mode 100644 index 0000000000000..c0c3085755311 --- /dev/null +++ b/tests/baselines/reference/classVarianceCircularity.symbols @@ -0,0 +1,42 @@ +=== tests/cases/compiler/classVarianceCircularity.ts === +// Issue #52813 + +function f() { +>f : Symbol(f, Decl(classVarianceCircularity.ts, 0, 0)) + + const b = new Bar(); +>b : Symbol(b, Decl(classVarianceCircularity.ts, 3, 9)) +>Bar : Symbol(Bar, Decl(classVarianceCircularity.ts, 6, 1)) + + // Uncomment to create error + console.log(b.Value); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>b.Value : Symbol(Bar.Value, Decl(classVarianceCircularity.ts, 11, 43)) +>b : Symbol(b, Decl(classVarianceCircularity.ts, 3, 9)) +>Value : Symbol(Bar.Value, Decl(classVarianceCircularity.ts, 11, 43)) +} + +class Bar { +>Bar : Symbol(Bar, Decl(classVarianceCircularity.ts, 6, 1)) +>T : Symbol(T, Decl(classVarianceCircularity.ts, 8, 10)) + + num!: number; +>num : Symbol(Bar.num, Decl(classVarianceCircularity.ts, 8, 14)) + + // Or swap these two lines + Field: number = (this as Bar).num; +>Field : Symbol(Bar.Field, Decl(classVarianceCircularity.ts, 9, 17)) +>(this as Bar).num : Symbol(Bar.num, Decl(classVarianceCircularity.ts, 8, 14)) +>this : Symbol(Bar, Decl(classVarianceCircularity.ts, 6, 1)) +>Bar : Symbol(Bar, Decl(classVarianceCircularity.ts, 6, 1)) +>num : Symbol(Bar.num, Decl(classVarianceCircularity.ts, 8, 14)) + + Value = (this as Bar).num; +>Value : Symbol(Bar.Value, Decl(classVarianceCircularity.ts, 11, 43)) +>(this as Bar).num : Symbol(Bar.num, Decl(classVarianceCircularity.ts, 8, 14)) +>this : Symbol(Bar, Decl(classVarianceCircularity.ts, 6, 1)) +>Bar : Symbol(Bar, Decl(classVarianceCircularity.ts, 6, 1)) +>num : Symbol(Bar.num, Decl(classVarianceCircularity.ts, 8, 14)) +} diff --git a/tests/baselines/reference/classVarianceCircularity.types b/tests/baselines/reference/classVarianceCircularity.types new file mode 100644 index 0000000000000..8a26ba4940a5b --- /dev/null +++ b/tests/baselines/reference/classVarianceCircularity.types @@ -0,0 +1,45 @@ +=== tests/cases/compiler/classVarianceCircularity.ts === +// Issue #52813 + +function f() { +>f : () => void + + const b = new Bar(); +>b : Bar +>new Bar() : Bar +>Bar : typeof Bar + + // Uncomment to create error + console.log(b.Value); +>console.log(b.Value) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>b.Value : any +>b : Bar +>Value : any +} + +class Bar { +>Bar : Bar + + num!: number; +>num : number + + // Or swap these two lines + Field: number = (this as Bar).num; +>Field : number +>(this as Bar).num : number +>(this as Bar) : Bar +>this as Bar : Bar +>this : this +>num : number + + Value = (this as Bar).num; +>Value : any +>(this as Bar).num : number +>(this as Bar) : Bar +>this as Bar : Bar +>this : this +>num : number +} diff --git a/tests/cases/compiler/classVarianceCircularity.ts b/tests/cases/compiler/classVarianceCircularity.ts new file mode 100644 index 0000000000000..3c92600d4197d --- /dev/null +++ b/tests/cases/compiler/classVarianceCircularity.ts @@ -0,0 +1,16 @@ +// @strict: true + +// Issue #52813 + +function f() { + const b = new Bar(); + // Uncomment to create error + console.log(b.Value); +} + +class Bar { + num!: number; + // Or swap these two lines + Field: number = (this as Bar).num; + Value = (this as Bar).num; +} \ No newline at end of file From 46c61b304a8be3d4b96096277b102094433c0b50 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Tue, 14 Mar 2023 16:39:38 -0700 Subject: [PATCH 02/11] WIP: defer type comparability check in assertions --- src/compiler/checker.ts | 90 +++++++++++++++---- .../classVarianceCircularity.errors.txt | 20 ----- .../reference/classVarianceCircularity.types | 6 +- ...btypeReductionNoStructuralCheck.errors.txt | 19 ---- ...ialSubtypeReductionNoStructuralCheck.types | 2 +- 5 files changed, 76 insertions(+), 61 deletions(-) delete mode 100644 tests/baselines/reference/classVarianceCircularity.errors.txt delete mode 100644 tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2b702364c0f28..589be3376994d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24,6 +24,7 @@ import { arrayToMultiMap, ArrayTypeNode, ArrowFunction, + AsExpression, AssertionExpression, AssignmentDeclarationKind, AssignmentKind, @@ -758,6 +759,7 @@ import { JSDocSatisfiesTag, JSDocSignature, JSDocTemplateTag, + JSDocTypeAssertion, JSDocTypedefTag, JSDocTypeExpression, JSDocTypeLiteral, @@ -1033,7 +1035,6 @@ import { TypeReferenceSerializationKind, TypeReferenceType, TypeVariable, - UnaryExpression, unescapeLeadingUnderscores, UnionOrIntersectionType, UnionOrIntersectionTypeNode, @@ -34210,7 +34211,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { grammarErrorOnNode(node, Diagnostics.This_syntax_is_reserved_in_files_with_the_mts_or_cts_extension_Use_an_as_expression_instead); } } - return checkAssertionWorker(node, node.type, node.expression); + // return checkAssertionWorker(node, node.type, node.expression); + return checkAssertionWorker(node); } function isValidConstAssertionArgument(node: Node): boolean { @@ -34241,27 +34243,74 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return false; } - function checkAssertionWorker(errNode: Node, type: TypeNode, expression: UnaryExpression | Expression, checkMode?: CheckMode) { - let exprType = checkExpression(expression, checkMode); + function checkAssertionWorker(node: JSDocTypeAssertion | AssertionExpression) { + // TODO: maybe we don't need this? maybe can be helper? + let type: TypeNode; + let expression: Expression; + switch (node.kind) { + case SyntaxKind.AsExpression: + case SyntaxKind.TypeAssertionExpression: + type = node.type; + expression = node.expression; + break; + case SyntaxKind.ParenthesizedExpression: + type = getJSDocTypeAssertionType(node); + expression = node.expression; + break; + } + + // function checkAssertionWorker(errNode: Node, type: TypeNode, expression: UnaryExpression | Expression, checkMode?: CheckMode) { + // let exprType = checkExpression(expression, checkMode); if (isConstTypeReference(type)) { if (!isValidConstAssertionArgument(expression)) { error(expression, Diagnostics.A_const_assertions_can_only_be_applied_to_references_to_enum_members_or_string_number_boolean_array_or_object_literals); } - return getRegularTypeOfLiteralType(exprType); + // return getRegularTypeOfLiteralType(exprType); + return getRegularTypeOfLiteralType(checkExpression(expression)); } checkSourceElement(type); - exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(exprType)); + checkNodeDeferred(node); // >> TODO: get node + // exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(exprType)); + // const targetType = getTypeFromTypeNode(type); + // if (!isErrorType(targetType)) { + // addLazyDiagnostic(() => { // TODO: defer this check + // const widenedType = getWidenedType(exprType); + // if (!isTypeComparableTo(targetType, widenedType)) { + // checkTypeComparableTo(exprType, targetType, errNode, + // Diagnostics.Conversion_of_type_0_to_type_1_may_be_a_mistake_because_neither_type_sufficiently_overlaps_with_the_other_If_this_was_intentional_convert_the_expression_to_unknown_first); + // } + // }); + // } + // return targetType; + return getTypeFromTypeNode(type); + } + + function checkAssertionDeferred(node: JSDocTypeAssertion | AssertionExpression) { + let type: TypeNode; + let expression: Expression; + let errNode: Node; + switch (node.kind) { + case SyntaxKind.AsExpression: + case SyntaxKind.TypeAssertionExpression: + type = (node as TypeAssertion | AsExpression).type; + expression = (node as TypeAssertion | AsExpression).expression; + errNode = node; + break; + case SyntaxKind.ParenthesizedExpression: + type = getJSDocTypeAssertionType(node); + expression = node.expression; + errNode = type; + break; + } + const exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(checkExpression(expression))); const targetType = getTypeFromTypeNode(type); if (!isErrorType(targetType)) { - addLazyDiagnostic(() => { - const widenedType = getWidenedType(exprType); - if (!isTypeComparableTo(targetType, widenedType)) { - checkTypeComparableTo(exprType, targetType, errNode, - Diagnostics.Conversion_of_type_0_to_type_1_may_be_a_mistake_because_neither_type_sufficiently_overlaps_with_the_other_If_this_was_intentional_convert_the_expression_to_unknown_first); - } - }); + const widenedType = getWidenedType(exprType); + if (!isTypeComparableTo(targetType, widenedType)) { + checkTypeComparableTo(exprType, targetType, errNode, + Diagnostics.Conversion_of_type_0_to_type_1_may_be_a_mistake_because_neither_type_sufficiently_overlaps_with_the_other_If_this_was_intentional_convert_the_expression_to_unknown_first); + } } - return targetType; } function checkNonNullChain(node: NonNullChain) { @@ -37483,8 +37532,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return checkSatisfiesExpressionWorker(node.expression, getJSDocSatisfiesExpressionType(node), checkMode); } if (isJSDocTypeAssertion(node)) { - const type = getJSDocTypeAssertionType(node); - return checkAssertionWorker(type, type, node.expression, checkMode); + // const type = getJSDocTypeAssertionType(node); + // return checkAssertionWorker(type, type, node.expression); + return checkAssertionWorker(node); } } return checkExpression(node.expression, checkMode); @@ -44606,7 +44656,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // Here, performing a full type check of the body of the function expression whilst in the process of // determining the type of foo would cause foo to be given type any because of the recursive reference. // Delaying the type check of the body ensures foo has been assigned a type. - function checkNodeDeferred(node: Node) { + function checkNodeDeferred(node: Node) { // >> const enclosingFile = getSourceFileOfNode(node); const links = getNodeLinks(enclosingFile); if (!(links.flags & NodeCheckFlags.TypeChecked)) { @@ -44626,7 +44676,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { links.deferredNodes = undefined; } - function checkDeferredNode(node: Node) { + function checkDeferredNode(node: Node) { // >> tracing?.push(tracing.Phase.Check, "checkDeferredNode", { kind: node.kind, pos: node.pos, end: node.end, path: (node as TracingNode).tracingPath }); const saveCurrentNode = currentNode; currentNode = node; @@ -44664,6 +44714,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { case SyntaxKind.JsxElement: checkJsxElementDeferred(node as JsxElement); break; + case SyntaxKind.TypeAssertionExpression: + case SyntaxKind.AsExpression: + case SyntaxKind.ParenthesizedExpression: + checkAssertionDeferred(node as AssertionExpression | JSDocTypeAssertion); } currentNode = saveCurrentNode; tracing?.pop(); diff --git a/tests/baselines/reference/classVarianceCircularity.errors.txt b/tests/baselines/reference/classVarianceCircularity.errors.txt deleted file mode 100644 index 6d76e0a12af97..0000000000000 --- a/tests/baselines/reference/classVarianceCircularity.errors.txt +++ /dev/null @@ -1,20 +0,0 @@ -tests/cases/compiler/classVarianceCircularity.ts(13,5): error TS7022: 'Value' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. - - -==== tests/cases/compiler/classVarianceCircularity.ts (1 errors) ==== - // Issue #52813 - - function f() { - const b = new Bar(); - // Uncomment to create error - console.log(b.Value); - } - - class Bar { - num!: number; - // Or swap these two lines - Field: number = (this as Bar).num; - Value = (this as Bar).num; - ~~~~~ -!!! error TS7022: 'Value' 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/classVarianceCircularity.types b/tests/baselines/reference/classVarianceCircularity.types index 8a26ba4940a5b..664e5ef220490 100644 --- a/tests/baselines/reference/classVarianceCircularity.types +++ b/tests/baselines/reference/classVarianceCircularity.types @@ -15,9 +15,9 @@ function f() { >console.log : (...data: any[]) => void >console : Console >log : (...data: any[]) => void ->b.Value : any +>b.Value : number >b : Bar ->Value : any +>Value : number } class Bar { @@ -36,7 +36,7 @@ class Bar { >num : number Value = (this as Bar).num; ->Value : any +>Value : number >(this as Bar).num : number >(this as Bar) : Bar >this as Bar : Bar diff --git a/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.errors.txt b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.errors.txt deleted file mode 100644 index 279e221ad70c3..0000000000000 --- a/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.errors.txt +++ /dev/null @@ -1,19 +0,0 @@ -tests/cases/compiler/trivialSubtypeReductionNoStructuralCheck.ts(3,7): error TS7023: 'steps' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. - - -==== tests/cases/compiler/trivialSubtypeReductionNoStructuralCheck.ts (1 errors) ==== - declare const props: WizardStepProps; - export class Wizard { - get steps() { - ~~~~~ -!!! error TS7023: 'steps' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. - return { - wizard: this, - ...props, - } as WizardStepProps; - } - } - - export interface WizardStepProps { - wizard?: Wizard; - } \ No newline at end of file diff --git a/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.types b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.types index 41d7b304624d2..90528d2d493a1 100644 --- a/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.types +++ b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.types @@ -6,7 +6,7 @@ export class Wizard { >Wizard : Wizard get steps() { ->steps : any +>steps : WizardStepProps return { >{ wizard: this, ...props, } as WizardStepProps : WizardStepProps From 3a2d00567fd96c05dbc0dae8ae645c8244d60b90 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Thu, 16 Mar 2023 10:42:40 -0700 Subject: [PATCH 03/11] add test not fixed by change --- ...classVarianceResolveCircularity.errors.txt | 15 ++++++++ .../classVarianceResolveCircularity.js | 21 ++++++++++++ .../classVarianceResolveCircularity.symbols | 34 +++++++++++++++++++ .../classVarianceResolveCircularity.types | 33 ++++++++++++++++++ .../classVarianceResolveCircularity.ts | 11 ++++++ 5 files changed, 114 insertions(+) create mode 100644 tests/baselines/reference/classVarianceResolveCircularity.errors.txt create mode 100644 tests/baselines/reference/classVarianceResolveCircularity.js create mode 100644 tests/baselines/reference/classVarianceResolveCircularity.symbols create mode 100644 tests/baselines/reference/classVarianceResolveCircularity.types create mode 100644 tests/cases/compiler/classVarianceResolveCircularity.ts diff --git a/tests/baselines/reference/classVarianceResolveCircularity.errors.txt b/tests/baselines/reference/classVarianceResolveCircularity.errors.txt new file mode 100644 index 0000000000000..faff1bf64260a --- /dev/null +++ b/tests/baselines/reference/classVarianceResolveCircularity.errors.txt @@ -0,0 +1,15 @@ +tests/cases/compiler/classVarianceResolveCircularity.ts(5,5): error TS7022: 'Value' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. + + +==== tests/cases/compiler/classVarianceResolveCircularity.ts (1 errors) ==== + // Issue #52813 + + class Bar { + num!: number; // Swap to remove error + Value = callme(this).num; + ~~~~~ +!!! error TS7022: 'Value' implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer. + Field: number = callme(this).num; + } + declare function callme(x: Bar): Bar; + declare function callme(x: object): string; \ No newline at end of file diff --git a/tests/baselines/reference/classVarianceResolveCircularity.js b/tests/baselines/reference/classVarianceResolveCircularity.js new file mode 100644 index 0000000000000..2518fa84c7bdc --- /dev/null +++ b/tests/baselines/reference/classVarianceResolveCircularity.js @@ -0,0 +1,21 @@ +//// [classVarianceResolveCircularity.ts] +// Issue #52813 + +class Bar { + num!: number; // Swap to remove error + Value = callme(this).num; + Field: number = callme(this).num; +} +declare function callme(x: Bar): Bar; +declare function callme(x: object): string; + +//// [classVarianceResolveCircularity.js] +"use strict"; +// Issue #52813 +var Bar = /** @class */ (function () { + function Bar() { + this.Value = callme(this).num; + this.Field = callme(this).num; + } + return Bar; +}()); diff --git a/tests/baselines/reference/classVarianceResolveCircularity.symbols b/tests/baselines/reference/classVarianceResolveCircularity.symbols new file mode 100644 index 0000000000000..a3bf2bc878ff6 --- /dev/null +++ b/tests/baselines/reference/classVarianceResolveCircularity.symbols @@ -0,0 +1,34 @@ +=== tests/cases/compiler/classVarianceResolveCircularity.ts === +// Issue #52813 + +class Bar { +>Bar : Symbol(Bar, Decl(classVarianceResolveCircularity.ts, 0, 0)) +>T : Symbol(T, Decl(classVarianceResolveCircularity.ts, 2, 10)) + + num!: number; // Swap to remove error +>num : Symbol(Bar.num, Decl(classVarianceResolveCircularity.ts, 2, 14)) + + Value = callme(this).num; +>Value : Symbol(Bar.Value, Decl(classVarianceResolveCircularity.ts, 3, 17)) +>callme(this).num : Symbol(Bar.num, Decl(classVarianceResolveCircularity.ts, 2, 14)) +>callme : Symbol(callme, Decl(classVarianceResolveCircularity.ts, 6, 1), Decl(classVarianceResolveCircularity.ts, 7, 47)) +>this : Symbol(Bar, Decl(classVarianceResolveCircularity.ts, 0, 0)) +>num : Symbol(Bar.num, Decl(classVarianceResolveCircularity.ts, 2, 14)) + + Field: number = callme(this).num; +>Field : Symbol(Bar.Field, Decl(classVarianceResolveCircularity.ts, 4, 29)) +>callme(this).num : Symbol(Bar.num, Decl(classVarianceResolveCircularity.ts, 2, 14)) +>callme : Symbol(callme, Decl(classVarianceResolveCircularity.ts, 6, 1), Decl(classVarianceResolveCircularity.ts, 7, 47)) +>this : Symbol(Bar, Decl(classVarianceResolveCircularity.ts, 0, 0)) +>num : Symbol(Bar.num, Decl(classVarianceResolveCircularity.ts, 2, 14)) +} +declare function callme(x: Bar): Bar; +>callme : Symbol(callme, Decl(classVarianceResolveCircularity.ts, 6, 1), Decl(classVarianceResolveCircularity.ts, 7, 47)) +>x : Symbol(x, Decl(classVarianceResolveCircularity.ts, 7, 24)) +>Bar : Symbol(Bar, Decl(classVarianceResolveCircularity.ts, 0, 0)) +>Bar : Symbol(Bar, Decl(classVarianceResolveCircularity.ts, 0, 0)) + +declare function callme(x: object): string; +>callme : Symbol(callme, Decl(classVarianceResolveCircularity.ts, 6, 1), Decl(classVarianceResolveCircularity.ts, 7, 47)) +>x : Symbol(x, Decl(classVarianceResolveCircularity.ts, 8, 24)) + diff --git a/tests/baselines/reference/classVarianceResolveCircularity.types b/tests/baselines/reference/classVarianceResolveCircularity.types new file mode 100644 index 0000000000000..d7e48dffe67df --- /dev/null +++ b/tests/baselines/reference/classVarianceResolveCircularity.types @@ -0,0 +1,33 @@ +=== tests/cases/compiler/classVarianceResolveCircularity.ts === +// Issue #52813 + +class Bar { +>Bar : Bar + + num!: number; // Swap to remove error +>num : number + + Value = callme(this).num; +>Value : any +>callme(this).num : number +>callme(this) : Bar +>callme : { (x: Bar): Bar; (x: object): string; } +>this : this +>num : number + + Field: number = callme(this).num; +>Field : number +>callme(this).num : number +>callme(this) : Bar +>callme : { (x: Bar): Bar; (x: object): string; } +>this : this +>num : number +} +declare function callme(x: Bar): Bar; +>callme : { (x: Bar): Bar; (x: object): string; } +>x : Bar + +declare function callme(x: object): string; +>callme : { (x: Bar): Bar; (x: object): string; } +>x : object + diff --git a/tests/cases/compiler/classVarianceResolveCircularity.ts b/tests/cases/compiler/classVarianceResolveCircularity.ts new file mode 100644 index 0000000000000..abff110eea799 --- /dev/null +++ b/tests/cases/compiler/classVarianceResolveCircularity.ts @@ -0,0 +1,11 @@ +// @strict: true + +// Issue #52813 + +class Bar { + num!: number; // Swap to remove error + Value = callme(this).num; + Field: number = callme(this).num; +} +declare function callme(x: Bar): Bar; +declare function callme(x: object): string; \ No newline at end of file From 2c4d61cbfd91e0e8a924a0f27f55a6adc831d57c Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Thu, 16 Mar 2023 13:38:07 -0700 Subject: [PATCH 04/11] clean up comments --- src/compiler/checker.ts | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c19fe5056c1ca..01658fbe07a87 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -34275,7 +34275,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { grammarErrorOnNode(node, Diagnostics.This_syntax_is_reserved_in_files_with_the_mts_or_cts_extension_Use_an_as_expression_instead); } } - // return checkAssertionWorker(node, node.type, node.expression); return checkAssertionWorker(node); } @@ -34323,29 +34322,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { break; } - // function checkAssertionWorker(errNode: Node, type: TypeNode, expression: UnaryExpression | Expression, checkMode?: CheckMode) { - // let exprType = checkExpression(expression, checkMode); if (isConstTypeReference(type)) { if (!isValidConstAssertionArgument(expression)) { error(expression, Diagnostics.A_const_assertions_can_only_be_applied_to_references_to_enum_members_or_string_number_boolean_array_or_object_literals); } - // return getRegularTypeOfLiteralType(exprType); return getRegularTypeOfLiteralType(checkExpression(expression)); } checkSourceElement(type); - checkNodeDeferred(node); // >> TODO: get node - // exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(exprType)); - // const targetType = getTypeFromTypeNode(type); - // if (!isErrorType(targetType)) { - // addLazyDiagnostic(() => { // TODO: defer this check - // const widenedType = getWidenedType(exprType); - // if (!isTypeComparableTo(targetType, widenedType)) { - // checkTypeComparableTo(exprType, targetType, errNode, - // Diagnostics.Conversion_of_type_0_to_type_1_may_be_a_mistake_because_neither_type_sufficiently_overlaps_with_the_other_If_this_was_intentional_convert_the_expression_to_unknown_first); - // } - // }); - // } - // return targetType; + checkNodeDeferred(node); return getTypeFromTypeNode(type); } @@ -37603,8 +37587,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return checkSatisfiesExpressionWorker(node.expression, getJSDocSatisfiesExpressionType(node), checkMode); } if (isJSDocTypeAssertion(node)) { - // const type = getJSDocTypeAssertionType(node); - // return checkAssertionWorker(type, type, node.expression); return checkAssertionWorker(node); } } From d409b7f3ea18074feff6720c45803a1cdb1bb535 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Thu, 16 Mar 2023 13:40:50 -0700 Subject: [PATCH 05/11] test: always call checkExpression --- src/compiler/checker.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 01658fbe07a87..4e379136e3dea 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -34322,11 +34322,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { break; } + const exprType = checkExpression(expression); if (isConstTypeReference(type)) { if (!isValidConstAssertionArgument(expression)) { error(expression, Diagnostics.A_const_assertions_can_only_be_applied_to_references_to_enum_members_or_string_number_boolean_array_or_object_literals); } - return getRegularTypeOfLiteralType(checkExpression(expression)); + return getRegularTypeOfLiteralType(exprType); } checkSourceElement(type); checkNodeDeferred(node); From 469f24d6d7d6609073823ad7a9c727702fd99c04 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Fri, 17 Mar 2023 13:05:08 -0700 Subject: [PATCH 06/11] WIP: use checkExpressionCached --- src/compiler/checker.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4e379136e3dea..b963332e0046c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -34322,12 +34322,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { break; } - const exprType = checkExpression(expression); + // const exprType = checkExpressionCached(expression, checkMode); if (isConstTypeReference(type)) { if (!isValidConstAssertionArgument(expression)) { error(expression, Diagnostics.A_const_assertions_can_only_be_applied_to_references_to_enum_members_or_string_number_boolean_array_or_object_literals); } - return getRegularTypeOfLiteralType(exprType); + return getRegularTypeOfLiteralType(checkExpression(expression)); } checkSourceElement(type); checkNodeDeferred(node); @@ -34351,7 +34351,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { errNode = type; break; } - const exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(checkExpression(expression))); + const exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(checkExpressionCached(expression))); const targetType = getTypeFromTypeNode(type); if (!isErrorType(targetType)) { const widenedType = getWidenedType(exprType); From 421b567269591f4264fac86d5fff886e8f078cf7 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Fri, 17 Mar 2023 13:43:08 -0700 Subject: [PATCH 07/11] always check expression when checking assertion --- src/compiler/checker.ts | 4 ++-- ...btypeReductionNoStructuralCheck.errors.txt | 19 +++++++++++++++++++ ...ialSubtypeReductionNoStructuralCheck.types | 2 +- 3 files changed, 22 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.errors.txt diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index b963332e0046c..2f5887e5c102b 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -34322,12 +34322,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { break; } - // const exprType = checkExpressionCached(expression, checkMode); + const exprType = checkExpressionCached(expression); if (isConstTypeReference(type)) { if (!isValidConstAssertionArgument(expression)) { error(expression, Diagnostics.A_const_assertions_can_only_be_applied_to_references_to_enum_members_or_string_number_boolean_array_or_object_literals); } - return getRegularTypeOfLiteralType(checkExpression(expression)); + return getRegularTypeOfLiteralType(exprType); } checkSourceElement(type); checkNodeDeferred(node); diff --git a/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.errors.txt b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.errors.txt new file mode 100644 index 0000000000000..279e221ad70c3 --- /dev/null +++ b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.errors.txt @@ -0,0 +1,19 @@ +tests/cases/compiler/trivialSubtypeReductionNoStructuralCheck.ts(3,7): error TS7023: 'steps' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. + + +==== tests/cases/compiler/trivialSubtypeReductionNoStructuralCheck.ts (1 errors) ==== + declare const props: WizardStepProps; + export class Wizard { + get steps() { + ~~~~~ +!!! error TS7023: 'steps' implicitly has return type 'any' because it does not have a return type annotation and is referenced directly or indirectly in one of its return expressions. + return { + wizard: this, + ...props, + } as WizardStepProps; + } + } + + export interface WizardStepProps { + wizard?: Wizard; + } \ No newline at end of file diff --git a/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.types b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.types index 90528d2d493a1..41d7b304624d2 100644 --- a/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.types +++ b/tests/baselines/reference/trivialSubtypeReductionNoStructuralCheck.types @@ -6,7 +6,7 @@ export class Wizard { >Wizard : Wizard get steps() { ->steps : WizardStepProps +>steps : any return { >{ wizard: this, ...props, } as WizardStepProps : WizardStepProps From 9e8f42437fc046ed1fba95a5aaf5b843e1ed5572 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Mon, 20 Mar 2023 15:46:18 -0700 Subject: [PATCH 08/11] add checkMode --- src/compiler/checker.ts | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 2f5887e5c102b..0b4ded90be51a 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -24,7 +24,6 @@ import { arrayToMultiMap, ArrayTypeNode, ArrowFunction, - AsExpression, AssertionExpression, AssignmentDeclarationKind, AssignmentKind, @@ -34268,14 +34267,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getReturnTypeOfSignature(signature); } - function checkAssertion(node: AssertionExpression) { + function checkAssertion(node: AssertionExpression, checkMode: CheckMode | undefined) { if (node.kind === SyntaxKind.TypeAssertionExpression) { const file = getSourceFileOfNode(node); if (file && fileExtensionIsOneOf(file.fileName, [Extension.Cts, Extension.Mts])) { grammarErrorOnNode(node, Diagnostics.This_syntax_is_reserved_in_files_with_the_mts_or_cts_extension_Use_an_as_expression_instead); } } - return checkAssertionWorker(node); + return checkAssertionWorker(node, checkMode); } function isValidConstAssertionArgument(node: Node): boolean { @@ -34306,8 +34305,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return false; } - function checkAssertionWorker(node: JSDocTypeAssertion | AssertionExpression) { - // TODO: maybe we don't need this? maybe can be helper? + function checkAssertionWorker(node: JSDocTypeAssertion | AssertionExpression, checkMode: CheckMode | undefined) { let type: TypeNode; let expression: Expression; switch (node.kind) { @@ -34322,7 +34320,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { break; } - const exprType = checkExpressionCached(expression); + const exprType = checkExpressionCached(expression, checkMode); if (isConstTypeReference(type)) { if (!isValidConstAssertionArgument(expression)) { error(expression, Diagnostics.A_const_assertions_can_only_be_applied_to_references_to_enum_members_or_string_number_boolean_array_or_object_literals); @@ -34341,8 +34339,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { switch (node.kind) { case SyntaxKind.AsExpression: case SyntaxKind.TypeAssertionExpression: - type = (node as TypeAssertion | AsExpression).type; - expression = (node as TypeAssertion | AsExpression).expression; + type = node.type; + expression = node.expression; errNode = node; break; case SyntaxKind.ParenthesizedExpression: @@ -37588,7 +37586,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return checkSatisfiesExpressionWorker(node.expression, getJSDocSatisfiesExpressionType(node), checkMode); } if (isJSDocTypeAssertion(node)) { - return checkAssertionWorker(node); + return checkAssertionWorker(node, checkMode); } } return checkExpression(node.expression, checkMode); @@ -37669,7 +37667,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return checkTypeOfExpression(node as TypeOfExpression); case SyntaxKind.TypeAssertionExpression: case SyntaxKind.AsExpression: - return checkAssertion(node as AssertionExpression); + return checkAssertion(node as AssertionExpression, checkMode); case SyntaxKind.NonNullExpression: return checkNonNullAssertion(node as NonNullExpression); case SyntaxKind.ExpressionWithTypeArguments: @@ -44711,7 +44709,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // Here, performing a full type check of the body of the function expression whilst in the process of // determining the type of foo would cause foo to be given type any because of the recursive reference. // Delaying the type check of the body ensures foo has been assigned a type. - function checkNodeDeferred(node: Node) { // >> + function checkNodeDeferred(node: Node) { const enclosingFile = getSourceFileOfNode(node); const links = getNodeLinks(enclosingFile); if (!(links.flags & NodeCheckFlags.TypeChecked)) { @@ -44731,7 +44729,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { links.deferredNodes = undefined; } - function checkDeferredNode(node: Node) { // >> + function checkDeferredNode(node: Node) { tracing?.push(tracing.Phase.Check, "checkDeferredNode", { kind: node.kind, pos: node.pos, end: node.end, path: (node as TracingNode).tracingPath }); const saveCurrentNode = currentNode; currentNode = node; From 7620056b590ee80f35efddfc71ad44bdfaac63e9 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Mon, 20 Mar 2023 15:53:24 -0700 Subject: [PATCH 09/11] readd lazy diagnostic call --- src/compiler/checker.ts | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0b4ded90be51a..44bb4e13ea96e 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -34352,11 +34352,13 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(checkExpressionCached(expression))); const targetType = getTypeFromTypeNode(type); if (!isErrorType(targetType)) { - const widenedType = getWidenedType(exprType); - if (!isTypeComparableTo(targetType, widenedType)) { - checkTypeComparableTo(exprType, targetType, errNode, - Diagnostics.Conversion_of_type_0_to_type_1_may_be_a_mistake_because_neither_type_sufficiently_overlaps_with_the_other_If_this_was_intentional_convert_the_expression_to_unknown_first); - } + addLazyDiagnostic(() => { + const widenedType = getWidenedType(exprType); + if (!isTypeComparableTo(targetType, widenedType)) { + checkTypeComparableTo(exprType, targetType, errNode, + Diagnostics.Conversion_of_type_0_to_type_1_may_be_a_mistake_because_neither_type_sufficiently_overlaps_with_the_other_If_this_was_intentional_convert_the_expression_to_unknown_first); + } + }); } } From fd7d37a9b4e8134db7d40a2e3da5d83d0e58aaf0 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Tue, 21 Mar 2023 16:06:15 -0700 Subject: [PATCH 10/11] use checkExpression instead of checkExpressionCached --- src/compiler/checker.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 44bb4e13ea96e..1c4afb145872c 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -34320,7 +34320,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { break; } - const exprType = checkExpressionCached(expression, checkMode); + const exprType = checkExpression(expression, checkMode); if (isConstTypeReference(type)) { if (!isValidConstAssertionArgument(expression)) { error(expression, Diagnostics.A_const_assertions_can_only_be_applied_to_references_to_enum_members_or_string_number_boolean_array_or_object_literals); @@ -34349,7 +34349,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { errNode = type; break; } - const exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(checkExpressionCached(expression))); + const exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(checkExpression(expression))); const targetType = getTypeFromTypeNode(type); if (!isErrorType(targetType)) { addLazyDiagnostic(() => { From 5171fafab36decb3b43026ed23ee8166e5af6d37 Mon Sep 17 00:00:00 2001 From: Gabriela Araujo Britto Date: Wed, 22 Mar 2023 15:09:21 -0700 Subject: [PATCH 11/11] extract duplicate code into helper --- src/compiler/checker.ts | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 1c4afb145872c..35b37c23910af 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -34306,20 +34306,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { } function checkAssertionWorker(node: JSDocTypeAssertion | AssertionExpression, checkMode: CheckMode | undefined) { - let type: TypeNode; - let expression: Expression; - switch (node.kind) { - case SyntaxKind.AsExpression: - case SyntaxKind.TypeAssertionExpression: - type = node.type; - expression = node.expression; - break; - case SyntaxKind.ParenthesizedExpression: - type = getJSDocTypeAssertionType(node); - expression = node.expression; - break; - } - + const { type, expression } = getAssertionTypeAndExpression(node); const exprType = checkExpression(expression, checkMode); if (isConstTypeReference(type)) { if (!isValidConstAssertionArgument(expression)) { @@ -34332,23 +34319,27 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return getTypeFromTypeNode(type); } - function checkAssertionDeferred(node: JSDocTypeAssertion | AssertionExpression) { + function getAssertionTypeAndExpression(node: JSDocTypeAssertion | AssertionExpression) { let type: TypeNode; let expression: Expression; - let errNode: Node; switch (node.kind) { case SyntaxKind.AsExpression: case SyntaxKind.TypeAssertionExpression: type = node.type; expression = node.expression; - errNode = node; break; case SyntaxKind.ParenthesizedExpression: type = getJSDocTypeAssertionType(node); expression = node.expression; - errNode = type; break; } + + return { type, expression }; + } + + function checkAssertionDeferred(node: JSDocTypeAssertion | AssertionExpression) { + const { type, expression } = getAssertionTypeAndExpression(node); + const errNode = isParenthesizedExpression(node) ? type : node; const exprType = getRegularTypeOfObjectLiteral(getBaseTypeOfLiteralType(checkExpression(expression))); const targetType = getTypeFromTypeNode(type); if (!isErrorType(targetType)) {