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:
Nathan Shively-Sanders 2019-07-25 10:23:03 -07:00 committed by GitHub
parent aa12ec440c
commit 772bee5e84
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 250 additions and 11 deletions

View file

@ -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,

View file

@ -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

View file

@ -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

View file

@ -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;

View file

@ -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))

View file

@ -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

View file

@ -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

View file

@ -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))

View file

@ -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

View file

@ -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

View file

@ -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