Dont look for properties of Object and Function type when looking to resolve named import from module with export= (#37964)

* Add tests

* Dont look at object or function type when looking for members of `export=` type to be resolved by named imports
Fixes #37165

* Create separate cache when skipping function and object property augmentation

* Lookup in both cache if not skipObjectFunctionPropertyAugment
This commit is contained in:
Sheetal Nandi
2020-11-03 12:22:30 -08:00
committed by GitHub
parent b00870ead4
commit 9c60d5a4d3
11 changed files with 291 additions and 12 deletions
+15 -11
View File
@@ -2689,7 +2689,7 @@ namespace ts {
let symbolFromVariable: Symbol | undefined;
// First check if module was specified with "export=". If so, get the member from the resolved type
if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports.get(InternalSymbolName.ExportEquals)) {
symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), name.escapedText);
symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), name.escapedText, /*skipObjectFunctionPropertyAugment*/ true);
}
else {
symbolFromVariable = getPropertyOfVariable(targetSymbol, name.escapedText);
@@ -11147,7 +11147,7 @@ namespace ts {
return getReducedType(getApparentType(getReducedType(type)));
}
function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: __String): Symbol | undefined {
function createUnionOrIntersectionProperty(containingType: UnionOrIntersectionType, name: __String, skipObjectFunctionPropertyAugment?: boolean): Symbol | undefined {
let singleProp: Symbol | undefined;
let propSet: ESMap<SymbolId, Symbol> | undefined;
let indexTypes: Type[] | undefined;
@@ -11159,7 +11159,7 @@ namespace ts {
for (const current of containingType.types) {
const type = getApparentType(current);
if (!(type === errorType || type.flags & TypeFlags.Never)) {
const prop = getPropertyOfType(type, name);
const prop = getPropertyOfType(type, name, skipObjectFunctionPropertyAugment);
const modifiers = prop ? getDeclarationModifierFlagsFromSymbol(prop) : 0;
if (prop) {
if (isUnion) {
@@ -11276,20 +11276,23 @@ namespace ts {
// constituents, in which case the isPartial flag is set when the containing type is union type. We need
// these partial properties when identifying discriminant properties, but otherwise they are filtered out
// and do not appear to be present in the union type.
function getUnionOrIntersectionProperty(type: UnionOrIntersectionType, name: __String): Symbol | undefined {
const properties = type.propertyCache || (type.propertyCache = createSymbolTable());
let property = properties.get(name);
function getUnionOrIntersectionProperty(type: UnionOrIntersectionType, name: __String, skipObjectFunctionPropertyAugment?: boolean): Symbol | undefined {
let property = type.propertyCacheWithoutObjectFunctionPropertyAugment?.get(name) ||
!skipObjectFunctionPropertyAugment ? type.propertyCache?.get(name) : undefined;
if (!property) {
property = createUnionOrIntersectionProperty(type, name);
property = createUnionOrIntersectionProperty(type, name, skipObjectFunctionPropertyAugment);
if (property) {
const properties = skipObjectFunctionPropertyAugment ?
type.propertyCacheWithoutObjectFunctionPropertyAugment ||= createSymbolTable() :
type.propertyCache ||= createSymbolTable();
properties.set(name, property);
}
}
return property;
}
function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: __String): Symbol | undefined {
const property = getUnionOrIntersectionProperty(type, name);
function getPropertyOfUnionOrIntersectionType(type: UnionOrIntersectionType, name: __String, skipObjectFunctionPropertyAugment?: boolean): Symbol | undefined {
const property = getUnionOrIntersectionProperty(type, name, skipObjectFunctionPropertyAugment);
// We need to filter out partial properties in union types
return property && !(getCheckFlags(property) & CheckFlags.ReadPartial) ? property : undefined;
}
@@ -11367,7 +11370,7 @@ namespace ts {
* @param type a type to look up property from
* @param name a name of property to look up in a given type
*/
function getPropertyOfType(type: Type, name: __String): Symbol | undefined {
function getPropertyOfType(type: Type, name: __String, skipObjectFunctionPropertyAugment?: boolean): Symbol | undefined {
type = getReducedApparentType(type);
if (type.flags & TypeFlags.Object) {
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
@@ -11375,6 +11378,7 @@ namespace ts {
if (symbol && symbolIsValue(symbol)) {
return symbol;
}
if (skipObjectFunctionPropertyAugment) return undefined;
const functionType = resolved === anyFunctionType ? globalFunctionType :
resolved.callSignatures.length ? globalCallableFunctionType :
resolved.constructSignatures.length ? globalNewableFunctionType :
@@ -11388,7 +11392,7 @@ namespace ts {
return getPropertyOfObjectType(globalObjectType, name);
}
if (type.flags & TypeFlags.UnionOrIntersection) {
return getPropertyOfUnionOrIntersectionType(<UnionOrIntersectionType>type, name);
return getPropertyOfUnionOrIntersectionType(<UnionOrIntersectionType>type, name, skipObjectFunctionPropertyAugment);
}
return undefined;
}
+3 -1
View File
@@ -5193,7 +5193,9 @@ namespace ts {
/* @internal */
objectFlags: ObjectFlags;
/* @internal */
propertyCache: SymbolTable; // Cache of resolved properties
propertyCache?: SymbolTable; // Cache of resolved properties
/* @internal */
propertyCacheWithoutObjectFunctionPropertyAugment?: SymbolTable; // Cache of resolved properties that does not augment function or object type properties
/* @internal */
resolvedProperties: Symbol[];
/* @internal */
@@ -0,0 +1,21 @@
tests/cases/compiler/bar.d.ts(1,10): error TS2305: Module '"./foo"' has no exported member 'Bar'.
==== tests/cases/compiler/foo.d.ts (0 errors) ====
export = Foo;
export as namespace Foo;
declare namespace Foo {
function foo();
}
declare global {
namespace Bar { }
}
==== tests/cases/compiler/bar.d.ts (1 errors) ====
import { Bar } from './foo';
~~~
!!! error TS2305: Module '"./foo"' has no exported member 'Bar'.
export = Bar;
export as namespace Bar;
@@ -0,0 +1,31 @@
=== tests/cases/compiler/foo.d.ts ===
export = Foo;
>Foo : Symbol(Foo, Decl(foo.d.ts, 1, 24))
export as namespace Foo;
>Foo : Symbol(Foo, Decl(foo.d.ts, 0, 13))
declare namespace Foo {
>Foo : Symbol(Foo, Decl(foo.d.ts, 1, 24))
function foo();
>foo : Symbol(foo, Decl(foo.d.ts, 3, 23))
}
declare global {
>global : Symbol(global, Decl(foo.d.ts, 5, 1))
namespace Bar { }
>Bar : Symbol(Bar, Decl(foo.d.ts, 7, 16))
}
=== tests/cases/compiler/bar.d.ts ===
import { Bar } from './foo';
>Bar : Symbol(Bar, Decl(bar.d.ts, 0, 8))
export = Bar;
>Bar : Symbol(Bar, Decl(bar.d.ts, 0, 8))
export as namespace Bar;
>Bar : Symbol(Bar, Decl(bar.d.ts, 1, 13))
@@ -0,0 +1,30 @@
=== tests/cases/compiler/foo.d.ts ===
export = Foo;
>Foo : typeof Foo
export as namespace Foo;
>Foo : typeof Foo
declare namespace Foo {
>Foo : typeof Foo
function foo();
>foo : () => any
}
declare global {
>global : any
namespace Bar { }
}
=== tests/cases/compiler/bar.d.ts ===
import { Bar } from './foo';
>Bar : any
export = Bar;
>Bar : any
export as namespace Bar;
>Bar : any
@@ -0,0 +1,37 @@
tests/cases/compiler/bar.ts(1,10): error TS2305: Module '"./foo"' has no exported member 'Bar'.
tests/cases/compiler/bar.ts(1,15): error TS2305: Module '"./foo"' has no exported member 'toString'.
tests/cases/compiler/bar.ts(3,10): error TS2305: Module '"./foo2"' has no exported member 'a'.
tests/cases/compiler/bar.ts(3,13): error TS2305: Module '"./foo2"' has no exported member 'b'.
tests/cases/compiler/bar.ts(3,19): error TS2305: Module '"./foo2"' has no exported member 'd'.
tests/cases/compiler/bar.ts(3,22): error TS2305: Module '"./foo2"' has no exported member 'toString'.
==== tests/cases/compiler/foo.d.ts (0 errors) ====
export = Foo;
export as namespace Foo;
declare namespace Foo {
function foo();
}
==== tests/cases/compiler/foo2.ts (0 errors) ====
let x: { a: string; c: string; } | { b: number; c: number; };
export = x
==== tests/cases/compiler/bar.ts (6 errors) ====
import { Bar, toString, foo } from './foo';
~~~
!!! error TS2305: Module '"./foo"' has no exported member 'Bar'.
~~~~~~~~
!!! error TS2305: Module '"./foo"' has no exported member 'toString'.
foo();
import { a, b, c, d, toString as foo2String } from './foo2';
~
!!! error TS2305: Module '"./foo2"' has no exported member 'a'.
~
!!! error TS2305: Module '"./foo2"' has no exported member 'b'.
~
!!! error TS2305: Module '"./foo2"' has no exported member 'd'.
~~~~~~~~
!!! error TS2305: Module '"./foo2"' has no exported member 'toString'.
c;
@@ -0,0 +1,31 @@
//// [tests/cases/compiler/namedImportNonExistentName.ts] ////
//// [foo.d.ts]
export = Foo;
export as namespace Foo;
declare namespace Foo {
function foo();
}
//// [foo2.ts]
let x: { a: string; c: string; } | { b: number; c: number; };
export = x
//// [bar.ts]
import { Bar, toString, foo } from './foo';
foo();
import { a, b, c, d, toString as foo2String } from './foo2';
c;
//// [foo2.js]
"use strict";
var x;
module.exports = x;
//// [bar.js]
"use strict";
exports.__esModule = true;
var foo_1 = require("./foo");
foo_1.foo();
var foo2_1 = require("./foo2");
foo2_1.c;
@@ -0,0 +1,44 @@
=== tests/cases/compiler/foo.d.ts ===
export = Foo;
>Foo : Symbol(Foo, Decl(foo.d.ts, 1, 24))
export as namespace Foo;
>Foo : Symbol(Foo, Decl(foo.d.ts, 0, 13))
declare namespace Foo {
>Foo : Symbol(Foo, Decl(foo.d.ts, 1, 24))
function foo();
>foo : Symbol(foo, Decl(foo.d.ts, 3, 23))
}
=== tests/cases/compiler/foo2.ts ===
let x: { a: string; c: string; } | { b: number; c: number; };
>x : Symbol(x, Decl(foo2.ts, 0, 3))
>a : Symbol(a, Decl(foo2.ts, 0, 8))
>c : Symbol(c, Decl(foo2.ts, 0, 19))
>b : Symbol(b, Decl(foo2.ts, 0, 36))
>c : Symbol(c, Decl(foo2.ts, 0, 47))
export = x
>x : Symbol(x, Decl(foo2.ts, 0, 3))
=== tests/cases/compiler/bar.ts ===
import { Bar, toString, foo } from './foo';
>Bar : Symbol(Bar, Decl(bar.ts, 0, 8))
>toString : Symbol(toString, Decl(bar.ts, 0, 13))
>foo : Symbol(foo, Decl(bar.ts, 0, 23))
foo();
>foo : Symbol(foo, Decl(bar.ts, 0, 23))
import { a, b, c, d, toString as foo2String } from './foo2';
>a : Symbol(a, Decl(bar.ts, 2, 8))
>b : Symbol(b, Decl(bar.ts, 2, 11))
>c : Symbol(c, Decl(bar.ts, 2, 14))
>d : Symbol(d, Decl(bar.ts, 2, 17))
>foo2String : Symbol(foo2String, Decl(bar.ts, 2, 20))
c;
>c : Symbol(c, Decl(bar.ts, 2, 14))
@@ -0,0 +1,46 @@
=== tests/cases/compiler/foo.d.ts ===
export = Foo;
>Foo : typeof Foo
export as namespace Foo;
>Foo : typeof Foo
declare namespace Foo {
>Foo : typeof Foo
function foo();
>foo : () => any
}
=== tests/cases/compiler/foo2.ts ===
let x: { a: string; c: string; } | { b: number; c: number; };
>x : { a: string; c: string; } | { b: number; c: number; }
>a : string
>c : string
>b : number
>c : number
export = x
>x : { a: string; c: string; } | { b: number; c: number; }
=== tests/cases/compiler/bar.ts ===
import { Bar, toString, foo } from './foo';
>Bar : any
>toString : any
>foo : () => any
foo();
>foo() : any
>foo : () => any
import { a, b, c, d, toString as foo2String } from './foo2';
>a : any
>b : any
>c : string | number
>d : any
>toString : any
>foo2String : any
c;
>c : string | number
@@ -0,0 +1,16 @@
// @filename: foo.d.ts
export = Foo;
export as namespace Foo;
declare namespace Foo {
function foo();
}
declare global {
namespace Bar { }
}
// @filename: bar.d.ts
import { Bar } from './foo';
export = Bar;
export as namespace Bar;
@@ -0,0 +1,17 @@
// @filename: foo.d.ts
export = Foo;
export as namespace Foo;
declare namespace Foo {
function foo();
}
// @filename: foo2.ts
let x: { a: string; c: string; } | { b: number; c: number; };
export = x
// @filename: bar.ts
import { Bar, toString, foo } from './foo';
foo();
import { a, b, c, d, toString as foo2String } from './foo2';
c;