diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts
index f56f56223cf..2f466267f9d 100644
--- a/src/compiler/checker.ts
+++ b/src/compiler/checker.ts
@@ -5396,7 +5396,7 @@ namespace ts {
return undefined;
}
- function getWidenedTypeFromAssignmentDeclaration(symbol: Symbol, resolvedSymbol?: Symbol) {
+ function getWidenedTypeForAssignmentDeclaration(symbol: Symbol, resolvedSymbol?: Symbol) {
// function/class/{} initializers are themselves containers, so they won't merge in the same way as other initializers
const container = getAssignedExpandoInitializer(symbol.valueDeclaration);
if (container) {
@@ -5429,7 +5429,7 @@ namespace ts {
}
}
if (!isCallExpression(expression)) {
- jsdocType = getJSDocTypeFromAssignmentDeclaration(jsdocType, expression, symbol, declaration);
+ jsdocType = getAnnotatedTypeForAssignmentDeclaration(jsdocType, expression, symbol, declaration);
}
if (!jsdocType) {
(types || (types = [])).push((isBinaryExpression(expression) || isCallExpression(expression)) ? getInitializerTypeFromAssignmentDeclaration(symbol, resolvedSymbol, expression, kind) : neverType);
@@ -5478,8 +5478,8 @@ namespace ts {
return type;
}
- function getJSDocTypeFromAssignmentDeclaration(declaredType: Type | undefined, expression: Expression, _symbol: Symbol, declaration: Declaration) {
- const typeNode = getJSDocType(expression.parent);
+ function getAnnotatedTypeForAssignmentDeclaration(declaredType: Type | undefined, expression: Expression, symbol: Symbol, declaration: Declaration) {
+ const typeNode = getEffectiveTypeAnnotationNode(expression.parent);
if (typeNode) {
const type = getWidenedType(getTypeFromTypeNode(typeNode));
if (!declaredType) {
@@ -5489,6 +5489,13 @@ namespace ts {
errorNextVariableOrPropertyDeclarationMustHaveSameType(/*firstDeclaration*/ undefined, declaredType, declaration, type);
}
}
+ if (symbol.parent) {
+ const typeNode = getEffectiveTypeAnnotationNode(symbol.parent.valueDeclaration);
+ if (typeNode) {
+ return getTypeOfPropertyOfType(getTypeFromTypeNode(typeNode), symbol.escapedName);
+ }
+ }
+
return declaredType;
}
@@ -5783,7 +5790,7 @@ namespace ts {
}
else if (isInJSFile(declaration) &&
(isCallExpression(declaration) || isBinaryExpression(declaration) || isPropertyAccessExpression(declaration) && isBinaryExpression(declaration.parent))) {
- type = getWidenedTypeFromAssignmentDeclaration(symbol);
+ type = getWidenedTypeForAssignmentDeclaration(symbol);
}
else if (isJSDocPropertyLikeTag(declaration)
|| isPropertyAccessExpression(declaration)
@@ -5798,7 +5805,7 @@ namespace ts {
return getTypeOfFuncClassEnumModule(symbol);
}
type = isBinaryExpression(declaration.parent) ?
- getWidenedTypeFromAssignmentDeclaration(symbol) :
+ getWidenedTypeForAssignmentDeclaration(symbol) :
tryGetTypeFromEffectiveTypeNode(declaration) || anyType;
}
else if (isPropertyAssignment(declaration)) {
@@ -5969,7 +5976,7 @@ namespace ts {
}
else if (declaration.kind === SyntaxKind.BinaryExpression ||
declaration.kind === SyntaxKind.PropertyAccessExpression && declaration.parent.kind === SyntaxKind.BinaryExpression) {
- return getWidenedTypeFromAssignmentDeclaration(symbol);
+ return getWidenedTypeForAssignmentDeclaration(symbol);
}
else if (symbol.flags & SymbolFlags.ValueModule && declaration && isSourceFile(declaration) && declaration.commonJsModuleIndicator) {
const resolvedModule = resolveExternalModuleSymbol(symbol);
@@ -5978,7 +5985,7 @@ namespace ts {
return errorType;
}
const exportEquals = getMergedSymbol(symbol.exports!.get(InternalSymbolName.ExportEquals)!);
- const type = getWidenedTypeFromAssignmentDeclaration(exportEquals, exportEquals === resolvedModule ? undefined : resolvedModule);
+ const type = getWidenedTypeForAssignmentDeclaration(exportEquals, exportEquals === resolvedModule ? undefined : resolvedModule);
if (!popTypeResolution()) {
return reportCircularityError(symbol);
}
@@ -19157,7 +19164,7 @@ namespace ts {
}
/**
- * Woah! Do you really want to use this function?
+ * Whoa! Do you really want to use this function?
*
* Unless you're trying to get the *non-apparent* type for a
* value-literal type or you're authoring relevant portions of this algorithm,
diff --git a/tests/baselines/reference/expandoFunctionContextualTypes.types b/tests/baselines/reference/expandoFunctionContextualTypes.types
index e1eca92659c..915b168776f 100644
--- a/tests/baselines/reference/expandoFunctionContextualTypes.types
+++ b/tests/baselines/reference/expandoFunctionContextualTypes.types
@@ -12,7 +12,7 @@ interface StatelessComponent
{
const MyComponent: StatelessComponent = () => null as any;
>MyComponent : StatelessComponent
->() => null as any : { (): any; defaultProps: { color: "red"; }; }
+>() => null as any : { (): any; defaultProps: Partial; }
>null as any : any
>null : null
diff --git a/tests/baselines/reference/expandoFunctionContextualTypesJs.types b/tests/baselines/reference/expandoFunctionContextualTypesJs.types
index 081c57c0dac..82442a9db4a 100644
--- a/tests/baselines/reference/expandoFunctionContextualTypesJs.types
+++ b/tests/baselines/reference/expandoFunctionContextualTypesJs.types
@@ -10,7 +10,7 @@
*/
const MyComponent = () => /* @type {any} */(null);
>MyComponent : { (): any; defaultProps?: Partial<{ color: "red" | "blue"; }>; }
->() => /* @type {any} */(null) : { (): any; defaultProps: { color: "red"; }; }
+>() => /* @type {any} */(null) : { (): any; defaultProps: Partial<{ color: "red" | "blue"; }>; }
>(null) : null
>null : null
diff --git a/tests/baselines/reference/propertyAssignmentUseParentType1.js b/tests/baselines/reference/propertyAssignmentUseParentType1.js
new file mode 100644
index 00000000000..5e839a31da4
--- /dev/null
+++ b/tests/baselines/reference/propertyAssignmentUseParentType1.js
@@ -0,0 +1,26 @@
+//// [propertyAssignmentUseParentType1.ts]
+interface N {
+ (): boolean
+ num: 123;
+}
+export const interfaced: N = () => true;
+interfaced.num = 123;
+
+export const inlined: { (): boolean; nun: 456 } = () => true;
+inlined.nun = 456;
+
+export const ignoreJsdoc = () => true;
+/** @type {string} make sure to ignore jsdoc! */
+ignoreJsdoc.extra = 111
+
+
+//// [propertyAssignmentUseParentType1.js]
+"use strict";
+exports.__esModule = true;
+exports.interfaced = function () { return true; };
+exports.interfaced.num = 123;
+exports.inlined = function () { return true; };
+exports.inlined.nun = 456;
+exports.ignoreJsdoc = function () { return true; };
+/** @type {string} make sure to ignore jsdoc! */
+exports.ignoreJsdoc.extra = 111;
diff --git a/tests/baselines/reference/propertyAssignmentUseParentType1.symbols b/tests/baselines/reference/propertyAssignmentUseParentType1.symbols
new file mode 100644
index 00000000000..081e2f12604
--- /dev/null
+++ b/tests/baselines/reference/propertyAssignmentUseParentType1.symbols
@@ -0,0 +1,35 @@
+=== tests/cases/conformance/salsa/propertyAssignmentUseParentType1.ts ===
+interface N {
+>N : Symbol(N, Decl(propertyAssignmentUseParentType1.ts, 0, 0))
+
+ (): boolean
+ num: 123;
+>num : Symbol(N.num, Decl(propertyAssignmentUseParentType1.ts, 1, 15))
+}
+export const interfaced: N = () => true;
+>interfaced : Symbol(interfaced, Decl(propertyAssignmentUseParentType1.ts, 4, 12), Decl(propertyAssignmentUseParentType1.ts, 4, 40))
+>N : Symbol(N, Decl(propertyAssignmentUseParentType1.ts, 0, 0))
+
+interfaced.num = 123;
+>interfaced.num : Symbol(N.num, Decl(propertyAssignmentUseParentType1.ts, 1, 15))
+>interfaced : Symbol(interfaced, Decl(propertyAssignmentUseParentType1.ts, 4, 12), Decl(propertyAssignmentUseParentType1.ts, 4, 40))
+>num : Symbol(N.num, Decl(propertyAssignmentUseParentType1.ts, 1, 15))
+
+export const inlined: { (): boolean; nun: 456 } = () => true;
+>inlined : Symbol(inlined, Decl(propertyAssignmentUseParentType1.ts, 7, 12), Decl(propertyAssignmentUseParentType1.ts, 7, 61))
+>nun : Symbol(nun, Decl(propertyAssignmentUseParentType1.ts, 7, 36))
+
+inlined.nun = 456;
+>inlined.nun : Symbol(nun, Decl(propertyAssignmentUseParentType1.ts, 7, 36))
+>inlined : Symbol(inlined, Decl(propertyAssignmentUseParentType1.ts, 7, 12), Decl(propertyAssignmentUseParentType1.ts, 7, 61))
+>nun : Symbol(nun, Decl(propertyAssignmentUseParentType1.ts, 7, 36))
+
+export const ignoreJsdoc = () => true;
+>ignoreJsdoc : Symbol(ignoreJsdoc, Decl(propertyAssignmentUseParentType1.ts, 10, 12), Decl(propertyAssignmentUseParentType1.ts, 10, 38))
+
+/** @type {string} make sure to ignore jsdoc! */
+ignoreJsdoc.extra = 111
+>ignoreJsdoc.extra : Symbol(ignoreJsdoc.extra, Decl(propertyAssignmentUseParentType1.ts, 10, 38))
+>ignoreJsdoc : Symbol(ignoreJsdoc, Decl(propertyAssignmentUseParentType1.ts, 10, 12), Decl(propertyAssignmentUseParentType1.ts, 10, 38))
+>extra : Symbol(ignoreJsdoc.extra, Decl(propertyAssignmentUseParentType1.ts, 10, 38))
+
diff --git a/tests/baselines/reference/propertyAssignmentUseParentType1.types b/tests/baselines/reference/propertyAssignmentUseParentType1.types
new file mode 100644
index 00000000000..f3bd5010b20
--- /dev/null
+++ b/tests/baselines/reference/propertyAssignmentUseParentType1.types
@@ -0,0 +1,44 @@
+=== tests/cases/conformance/salsa/propertyAssignmentUseParentType1.ts ===
+interface N {
+ (): boolean
+ num: 123;
+>num : 123
+}
+export const interfaced: N = () => true;
+>interfaced : N
+>() => true : { (): true; num: 123; }
+>true : true
+
+interfaced.num = 123;
+>interfaced.num = 123 : 123
+>interfaced.num : 123
+>interfaced : N
+>num : 123
+>123 : 123
+
+export const inlined: { (): boolean; nun: 456 } = () => true;
+>inlined : { (): boolean; nun: 456; }
+>nun : 456
+>() => true : { (): true; nun: 456; }
+>true : true
+
+inlined.nun = 456;
+>inlined.nun = 456 : 456
+>inlined.nun : 456
+>inlined : { (): boolean; nun: 456; }
+>nun : 456
+>456 : 456
+
+export const ignoreJsdoc = () => true;
+>ignoreJsdoc : { (): boolean; extra: number; }
+>() => true : { (): boolean; extra: number; }
+>true : true
+
+/** @type {string} make sure to ignore jsdoc! */
+ignoreJsdoc.extra = 111
+>ignoreJsdoc.extra = 111 : 111
+>ignoreJsdoc.extra : number
+>ignoreJsdoc : { (): boolean; extra: number; }
+>extra : number
+>111 : 111
+
diff --git a/tests/baselines/reference/propertyAssignmentUseParentType2.errors.txt b/tests/baselines/reference/propertyAssignmentUseParentType2.errors.txt
new file mode 100644
index 00000000000..b88e276b405
--- /dev/null
+++ b/tests/baselines/reference/propertyAssignmentUseParentType2.errors.txt
@@ -0,0 +1,24 @@
+tests/cases/conformance/salsa/propertyAssignmentUseParentType2.js(11,14): error TS2322: Type '{ (): boolean; nuo: 1000; }' is not assignable to type '{ (): boolean; nuo: 789; }'.
+ Types of property 'nuo' are incompatible.
+ Type '1000' is not assignable to type '789'.
+
+
+==== tests/cases/conformance/salsa/propertyAssignmentUseParentType2.js (1 errors) ====
+ /** @type {{ (): boolean; nuo: 789 }} */
+ export const inlined = () => true
+ inlined.nuo = 789
+
+ /** @type {{ (): boolean; nuo: 789 }} */
+ export const duplicated = () => true
+ /** @type {789} */
+ duplicated.nuo = 789
+
+ /** @type {{ (): boolean; nuo: 789 }} */
+ export const conflictingDuplicated = () => true
+ ~~~~~~~~~~~~~~~~~~~~~
+!!! error TS2322: Type '{ (): boolean; nuo: 1000; }' is not assignable to type '{ (): boolean; nuo: 789; }'.
+!!! error TS2322: Types of property 'nuo' are incompatible.
+!!! error TS2322: Type '1000' is not assignable to type '789'.
+ /** @type {1000} */
+ conflictingDuplicated.nuo = 789
+
\ No newline at end of file
diff --git a/tests/baselines/reference/propertyAssignmentUseParentType2.symbols b/tests/baselines/reference/propertyAssignmentUseParentType2.symbols
new file mode 100644
index 00000000000..93b8b9290da
--- /dev/null
+++ b/tests/baselines/reference/propertyAssignmentUseParentType2.symbols
@@ -0,0 +1,30 @@
+=== tests/cases/conformance/salsa/propertyAssignmentUseParentType2.js ===
+/** @type {{ (): boolean; nuo: 789 }} */
+export const inlined = () => true
+>inlined : Symbol(inlined, Decl(propertyAssignmentUseParentType2.js, 1, 12), Decl(propertyAssignmentUseParentType2.js, 1, 33))
+
+inlined.nuo = 789
+>inlined.nuo : Symbol(nuo, Decl(propertyAssignmentUseParentType2.js, 0, 25))
+>inlined : Symbol(inlined, Decl(propertyAssignmentUseParentType2.js, 1, 12), Decl(propertyAssignmentUseParentType2.js, 1, 33))
+>nuo : Symbol(nuo, Decl(propertyAssignmentUseParentType2.js, 0, 25))
+
+/** @type {{ (): boolean; nuo: 789 }} */
+export const duplicated = () => true
+>duplicated : Symbol(duplicated, Decl(propertyAssignmentUseParentType2.js, 5, 12), Decl(propertyAssignmentUseParentType2.js, 5, 36))
+
+/** @type {789} */
+duplicated.nuo = 789
+>duplicated.nuo : Symbol(nuo, Decl(propertyAssignmentUseParentType2.js, 4, 25))
+>duplicated : Symbol(duplicated, Decl(propertyAssignmentUseParentType2.js, 5, 12), Decl(propertyAssignmentUseParentType2.js, 5, 36))
+>nuo : Symbol(nuo, Decl(propertyAssignmentUseParentType2.js, 4, 25))
+
+/** @type {{ (): boolean; nuo: 789 }} */
+export const conflictingDuplicated = () => true
+>conflictingDuplicated : Symbol(conflictingDuplicated, Decl(propertyAssignmentUseParentType2.js, 10, 12), Decl(propertyAssignmentUseParentType2.js, 10, 47))
+
+/** @type {1000} */
+conflictingDuplicated.nuo = 789
+>conflictingDuplicated.nuo : Symbol(nuo, Decl(propertyAssignmentUseParentType2.js, 9, 25))
+>conflictingDuplicated : Symbol(conflictingDuplicated, Decl(propertyAssignmentUseParentType2.js, 10, 12), Decl(propertyAssignmentUseParentType2.js, 10, 47))
+>nuo : Symbol(nuo, Decl(propertyAssignmentUseParentType2.js, 9, 25))
+
diff --git a/tests/baselines/reference/propertyAssignmentUseParentType2.types b/tests/baselines/reference/propertyAssignmentUseParentType2.types
new file mode 100644
index 00000000000..24118a9ee73
--- /dev/null
+++ b/tests/baselines/reference/propertyAssignmentUseParentType2.types
@@ -0,0 +1,42 @@
+=== tests/cases/conformance/salsa/propertyAssignmentUseParentType2.js ===
+/** @type {{ (): boolean; nuo: 789 }} */
+export const inlined = () => true
+>inlined : { (): boolean; nuo: 789; }
+>() => true : { (): boolean; nuo: 789; }
+>true : true
+
+inlined.nuo = 789
+>inlined.nuo = 789 : 789
+>inlined.nuo : 789
+>inlined : { (): boolean; nuo: 789; }
+>nuo : 789
+>789 : 789
+
+/** @type {{ (): boolean; nuo: 789 }} */
+export const duplicated = () => true
+>duplicated : { (): boolean; nuo: 789; }
+>() => true : { (): boolean; nuo: 789; }
+>true : true
+
+/** @type {789} */
+duplicated.nuo = 789
+>duplicated.nuo = 789 : 789
+>duplicated.nuo : 789
+>duplicated : { (): boolean; nuo: 789; }
+>nuo : 789
+>789 : 789
+
+/** @type {{ (): boolean; nuo: 789 }} */
+export const conflictingDuplicated = () => true
+>conflictingDuplicated : { (): boolean; nuo: 789; }
+>() => true : { (): boolean; nuo: 1000; }
+>true : true
+
+/** @type {1000} */
+conflictingDuplicated.nuo = 789
+>conflictingDuplicated.nuo = 789 : 789
+>conflictingDuplicated.nuo : 789
+>conflictingDuplicated : { (): boolean; nuo: 789; }
+>nuo : 789
+>789 : 789
+
diff --git a/tests/cases/conformance/salsa/propertyAssignmentUseParentType1.ts b/tests/cases/conformance/salsa/propertyAssignmentUseParentType1.ts
new file mode 100644
index 00000000000..500ae6f2fe3
--- /dev/null
+++ b/tests/cases/conformance/salsa/propertyAssignmentUseParentType1.ts
@@ -0,0 +1,13 @@
+interface N {
+ (): boolean
+ num: 123;
+}
+export const interfaced: N = () => true;
+interfaced.num = 123;
+
+export const inlined: { (): boolean; nun: 456 } = () => true;
+inlined.nun = 456;
+
+export const ignoreJsdoc = () => true;
+/** @type {string} make sure to ignore jsdoc! */
+ignoreJsdoc.extra = 111
diff --git a/tests/cases/conformance/salsa/propertyAssignmentUseParentType2.ts b/tests/cases/conformance/salsa/propertyAssignmentUseParentType2.ts
new file mode 100644
index 00000000000..53696abbf81
--- /dev/null
+++ b/tests/cases/conformance/salsa/propertyAssignmentUseParentType2.ts
@@ -0,0 +1,18 @@
+// @allowJs: true
+// @checkJs: true
+// @noEmit: true
+// @Filename: propertyAssignmentUseParentType2.js
+
+/** @type {{ (): boolean; nuo: 789 }} */
+export const inlined = () => true
+inlined.nuo = 789
+
+/** @type {{ (): boolean; nuo: 789 }} */
+export const duplicated = () => true
+/** @type {789} */
+duplicated.nuo = 789
+
+/** @type {{ (): boolean; nuo: 789 }} */
+export const conflictingDuplicated = () => true
+/** @type {1000} */
+conflictingDuplicated.nuo = 789