Property assignment uses parent type annotation (#32553)
* Property assignment uses parent type annotation First draft, will write full explanation later. Also makes sure that jsdoc is ignored in TS. It was not before. * Update baselines
This commit is contained in:
parent
aa12ec440c
commit
772bee5e84
|
@ -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,
|
||||
|
|
|
@ -12,7 +12,7 @@ interface StatelessComponent<P> {
|
|||
|
||||
const MyComponent: StatelessComponent<MyComponentProps> = () => null as any;
|
||||
>MyComponent : StatelessComponent<MyComponentProps>
|
||||
>() => null as any : { (): any; defaultProps: { color: "red"; }; }
|
||||
>() => null as any : { (): any; defaultProps: Partial<MyComponentProps>; }
|
||||
>null as any : any
|
||||
>null : null
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
|
@ -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))
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -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))
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in a new issue