diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4eb2db0bd29..2371eccf8d5 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -28698,6 +28698,22 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { }); } + function getThisTypeOfObjectLiteralFromContextualType(containingLiteral: ObjectLiteralExpression, contextualType: Type | undefined) { + let literal = containingLiteral; + let type = contextualType; + while (type) { + const thisType = getThisTypeFromContextualType(type); + if (thisType) { + return thisType; + } + if (literal.parent.kind !== SyntaxKind.PropertyAssignment) { + break; + } + literal = literal.parent.parent as ObjectLiteralExpression; + type = getApparentTypeOfContextualType(literal, /*contextFlags*/ undefined); + } + } + function getContextualThisParameterType(func: SignatureDeclaration): Type | undefined { if (func.kind === SyntaxKind.ArrowFunction) { return undefined; @@ -28719,18 +28735,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // that includes a ThisType. If so, T is the contextual type for 'this'. We continue looking in // any directly enclosing object literals. const contextualType = getApparentTypeOfContextualType(containingLiteral, /*contextFlags*/ undefined); - let literal = containingLiteral; - let type = contextualType; - while (type) { - const thisType = getThisTypeFromContextualType(type); - if (thisType) { - return instantiateType(thisType, getMapperFromContext(getInferenceContext(containingLiteral))); - } - if (literal.parent.kind !== SyntaxKind.PropertyAssignment) { - break; - } - literal = literal.parent.parent as ObjectLiteralExpression; - type = getApparentTypeOfContextualType(literal, /*contextFlags*/ undefined); + const thisType = getThisTypeOfObjectLiteralFromContextualType(containingLiteral, contextualType); + if (thisType) { + return instantiateType(thisType, getMapperFromContext(getInferenceContext(containingLiteral))); } // There was no contextual ThisType for the containing object literal, so the contextual type // for 'this' is the non-null form of the contextual type for the containing object literal or @@ -45455,7 +45462,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { const containingLiteral = getContainingObjectLiteral(container); if (containingLiteral) { const contextualType = getApparentTypeOfContextualType(containingLiteral, /*contextFlags*/ undefined); - const type = contextualType && getThisTypeFromContextualType(contextualType); + const type = getThisTypeOfObjectLiteralFromContextualType(containingLiteral, contextualType); return type && !isTypeAny(type); } } diff --git a/tests/baselines/reference/findAllRefsJsThisPropertyAssignment2.baseline.jsonc b/tests/baselines/reference/findAllRefsJsThisPropertyAssignment2.baseline.jsonc new file mode 100644 index 00000000000..149223b85b0 --- /dev/null +++ b/tests/baselines/reference/findAllRefsJsThisPropertyAssignment2.baseline.jsonc @@ -0,0 +1,149 @@ +// === findAllReferences === +// === /tests/cases/fourslash/infer.d.ts === +// export declare function infer(o: { m: Record } & ThisType<{ <|[|{| isWriteAccess: true |}x|]: number|> }>): void; + +// === /tests/cases/fourslash/a.js === +// import { infer } from "./infer"; +// infer({ +// m: { +// initData() { +// <|this.[|{| isWriteAccess: true |}x|] = 1;|> +// this./*FIND ALL REFS*/[|x|]; +// }, +// } +// }); + +// === /tests/cases/fourslash/b.ts === +// import { infer } from "./infer"; +// infer({ +// m: { +// initData() { +// this.[|{| isWriteAccess: true |}x|] = 1; +// this.[|x|]; +// }, +// } +// }); + + // === Definitions === + // === /tests/cases/fourslash/infer.d.ts === + // export declare function infer(o: { m: Record } & ThisType<{ <|[|x|]: number|> }>): void; + + // === Details === + [ + { + "containerKind": "", + "containerName": "", + "kind": "property", + "name": "(property) x: number", + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "property", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "x", + "kind": "propertyName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "number", + "kind": "keyword" + } + ] + } + ] + + + +// === findAllReferences === +// === /tests/cases/fourslash/infer.d.ts === +// export declare function infer(o: { m: Record } & ThisType<{ <|[|{| isWriteAccess: true |}x|]: number|> }>): void; + +// === /tests/cases/fourslash/a.js === +// import { infer } from "./infer"; +// infer({ +// m: { +// initData() { +// <|this.[|{| isWriteAccess: true |}x|] = 1;|> +// this.[|x|]; +// }, +// } +// }); + +// === /tests/cases/fourslash/b.ts === +// import { infer } from "./infer"; +// infer({ +// m: { +// initData() { +// this.[|{| isWriteAccess: true |}x|] = 1; +// this./*FIND ALL REFS*/[|x|]; +// }, +// } +// }); + + // === Definitions === + // === /tests/cases/fourslash/infer.d.ts === + // export declare function infer(o: { m: Record } & ThisType<{ <|[|x|]: number|> }>): void; + + // === Details === + [ + { + "containerKind": "", + "containerName": "", + "kind": "property", + "name": "(property) x: number", + "displayParts": [ + { + "text": "(", + "kind": "punctuation" + }, + { + "text": "property", + "kind": "text" + }, + { + "text": ")", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "x", + "kind": "propertyName" + }, + { + "text": ":", + "kind": "punctuation" + }, + { + "text": " ", + "kind": "space" + }, + { + "text": "number", + "kind": "keyword" + } + ] + } + ] \ No newline at end of file diff --git a/tests/cases/fourslash/findAllRefsJsThisPropertyAssignment2.ts b/tests/cases/fourslash/findAllRefsJsThisPropertyAssignment2.ts new file mode 100644 index 00000000000..6c04e60df5e --- /dev/null +++ b/tests/cases/fourslash/findAllRefsJsThisPropertyAssignment2.ts @@ -0,0 +1,31 @@ +/// + +// @allowJs: true +// @noImplicitThis: true + +// @Filename: infer.d.ts +//// export declare function infer(o: { m: Record } & ThisType<{ x: number }>): void; + +// @Filename: a.js +//// import { infer } from "./infer"; +//// infer({ +//// m: { +//// initData() { +//// this.x = 1; +//// this./*1*/x; +//// }, +//// } +//// }); + +// @Filename: b.ts +//// import { infer } from "./infer"; +//// infer({ +//// m: { +//// initData() { +//// this.x = 1; +//// this./*2*/x; +//// }, +//// } +//// }); + +verify.baselineFindAllReferences("1", "2");