Skip to content

Commit 5c2febf

Browse files
Allow referencing type-only exports as namespace members in ImportTypes and TypeQueries (microsoft#49056)
* Allow referencing type-only exports as namespace members in ImportTypes and TypeQueries * Add extra test case * ;; -> ; * undefined -> false Co-authored-by: Daniel Rosenwasser <[email protected]>
1 parent 3c87170 commit 5c2febf

10 files changed

+230
-13
lines changed

src/compiler/checker.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4110,8 +4110,10 @@ namespace ts {
41104110
return getMergedSymbol(symbol && (symbol.flags & SymbolFlags.ExportValue) !== 0 && symbol.exportSymbol || symbol);
41114111
}
41124112

4113-
function symbolIsValue(symbol: Symbol): boolean {
4114-
return !!(symbol.flags & SymbolFlags.Value || symbol.flags & SymbolFlags.Alias && resolveAlias(symbol).flags & SymbolFlags.Value && !getTypeOnlyAliasDeclaration(symbol));
4113+
function symbolIsValue(symbol: Symbol, includeTypeOnlyMembers?: boolean): boolean {
4114+
return !!(
4115+
symbol.flags & SymbolFlags.Value ||
4116+
symbol.flags & SymbolFlags.Alias && resolveAlias(symbol).flags & SymbolFlags.Value && (includeTypeOnlyMembers || !getTypeOnlyAliasDeclaration(symbol)));
41154117
}
41164118

41174119
function findConstructorDeclaration(node: ClassLikeDeclaration): ConstructorDeclaration | undefined {
@@ -12575,12 +12577,12 @@ namespace ts {
1257512577
* @param type a type to look up property from
1257612578
* @param name a name of property to look up in a given type
1257712579
*/
12578-
function getPropertyOfType(type: Type, name: __String, skipObjectFunctionPropertyAugment?: boolean): Symbol | undefined {
12580+
function getPropertyOfType(type: Type, name: __String, skipObjectFunctionPropertyAugment?: boolean, includeTypeOnlyMembers?: boolean): Symbol | undefined {
1257912581
type = getReducedApparentType(type);
1258012582
if (type.flags & TypeFlags.Object) {
1258112583
const resolved = resolveStructuredTypeMembers(type as ObjectType);
1258212584
const symbol = resolved.members.get(name);
12583-
if (symbol && symbolIsValue(symbol)) {
12585+
if (symbol && symbolIsValue(symbol, includeTypeOnlyMembers)) {
1258412586
return symbol;
1258512587
}
1258612588
if (skipObjectFunctionPropertyAugment) return undefined;
@@ -16208,7 +16210,7 @@ namespace ts {
1620816210
// the `exports` lookup process that only looks up namespace members which is used for most type references
1620916211
const mergedResolvedSymbol = getMergedSymbol(resolveSymbol(currentNamespace));
1621016212
const next = node.isTypeOf
16211-
? getPropertyOfType(getTypeOfSymbol(mergedResolvedSymbol), current.escapedText)
16213+
? getPropertyOfType(getTypeOfSymbol(mergedResolvedSymbol), current.escapedText, /*skipObjectFunctionPropertyAugment*/ false, /*includeTypeOnlyMembers*/ true)
1621216214
: getSymbol(getExportsOfSymbol(mergedResolvedSymbol), current.escapedText, meaning);
1621316215
if (!next) {
1621416216
error(current, Diagnostics.Namespace_0_has_no_exported_member_1, getFullyQualifiedName(currentNamespace), declarationNameToString(current));
@@ -28995,9 +28997,9 @@ namespace ts {
2899528997
if (isIdentifier(left) && parentSymbol) {
2899628998
markAliasReferenced(parentSymbol, node);
2899728999
}
28998-
return isErrorType(apparentType) ? errorType : apparentType;;
29000+
return isErrorType(apparentType) ? errorType : apparentType;
2899929001
}
29000-
prop = getPropertyOfType(apparentType, right.escapedText);
29002+
prop = getPropertyOfType(apparentType, right.escapedText, /*skipObjectFunctionPropertyAugment*/ false, /*includeTypeOnlyMembers*/ node.kind === SyntaxKind.QualifiedName);
2900129003
}
2900229004
// In `Foo.Bar.Baz`, 'Foo' is not referenced if 'Bar' is a const enum or a module containing only const enums.
2900329005
// `Foo` is also not referenced in `enum FooCopy { Bar = Foo.Bar }`, because the enum member value gets inlined
Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
/b.ts(2,21): error TS2339: Property 'A' does not exist on type 'typeof import("/a")'.
1+
/b.ts(8,3): error TS2322: Type '{ A: any; B: any; }' is not assignable to type 'typeof import("/a")'.
2+
Object literal may only specify known properties, and 'A' does not exist in type 'typeof import("/a")'.
23

34

45
==== /a.ts (0 errors) ====
@@ -9,7 +10,15 @@
910
==== /b.ts (1 errors) ====
1011
import * as types from './a';
1112
let A: typeof types.A;
12-
~
13-
!!! error TS2339: Property 'A' does not exist on type 'typeof import("/a")'.
1413
let B: typeof types.B;
14+
15+
let t: typeof types = {
16+
// error: while you can ask for `typeof types.A`,
17+
// `typeof types` does not include `A`
18+
A: undefined as any,
19+
~~~~~~~~~~~~~~~~~~~
20+
!!! error TS2322: Type '{ A: any; B: any; }' is not assignable to type 'typeof import("/a")'.
21+
!!! error TS2322: Object literal may only specify known properties, and 'A' does not exist in type 'typeof import("/a")'.
22+
B: undefined as any,
23+
}
1524

tests/baselines/reference/namespaceImportTypeQuery.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,13 @@ export class B {};
99
import * as types from './a';
1010
let A: typeof types.A;
1111
let B: typeof types.B;
12+
13+
let t: typeof types = {
14+
// error: while you can ask for `typeof types.A`,
15+
// `typeof types` does not include `A`
16+
A: undefined as any,
17+
B: undefined as any,
18+
}
1219

1320

1421
//// [a.js]
@@ -32,3 +39,9 @@ exports.B = B;
3239
exports.__esModule = true;
3340
var A;
3441
var B;
42+
var t = {
43+
// error: while you can ask for `typeof types.A`,
44+
// `typeof types` does not include `A`
45+
A: undefined,
46+
B: undefined
47+
};

tests/baselines/reference/namespaceImportTypeQuery.symbols

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,28 @@ import * as types from './a';
1414

1515
let A: typeof types.A;
1616
>A : Symbol(A, Decl(b.ts, 1, 3))
17+
>types.A : Symbol(types.A, Decl(a.ts, 1, 13))
1718
>types : Symbol(types, Decl(b.ts, 0, 6))
19+
>A : Symbol(types.A, Decl(a.ts, 1, 13))
1820

1921
let B: typeof types.B;
2022
>B : Symbol(B, Decl(b.ts, 2, 3))
2123
>types.B : Symbol(types.B, Decl(a.ts, 1, 18))
2224
>types : Symbol(types, Decl(b.ts, 0, 6))
2325
>B : Symbol(types.B, Decl(a.ts, 1, 18))
2426

27+
let t: typeof types = {
28+
>t : Symbol(t, Decl(b.ts, 4, 3))
29+
>types : Symbol(types, Decl(b.ts, 0, 6))
30+
31+
// error: while you can ask for `typeof types.A`,
32+
// `typeof types` does not include `A`
33+
A: undefined as any,
34+
>A : Symbol(A, Decl(b.ts, 4, 23))
35+
>undefined : Symbol(undefined)
36+
37+
B: undefined as any,
38+
>B : Symbol(B, Decl(b.ts, 7, 22))
39+
>undefined : Symbol(undefined)
40+
}
41+

tests/baselines/reference/namespaceImportTypeQuery.types

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,32 @@ import * as types from './a';
1313
>types : typeof types
1414

1515
let A: typeof types.A;
16-
>A : any
17-
>types.A : any
16+
>A : typeof types.A
17+
>types.A : typeof types.A
1818
>types : typeof types
19-
>A : any
19+
>A : typeof types.A
2020

2121
let B: typeof types.B;
2222
>B : typeof types.B
2323
>types.B : typeof types.B
2424
>types : typeof types
2525
>B : typeof types.B
2626

27+
let t: typeof types = {
28+
>t : typeof types
29+
>types : typeof types
30+
>{ // error: while you can ask for `typeof types.A`, // `typeof types` does not include `A` A: undefined as any, B: undefined as any,} : { A: any; B: any; }
31+
32+
// error: while you can ask for `typeof types.A`,
33+
// `typeof types` does not include `A`
34+
A: undefined as any,
35+
>A : any
36+
>undefined as any : any
37+
>undefined : undefined
38+
39+
B: undefined as any,
40+
>B : any
41+
>undefined as any : any
42+
>undefined : undefined
43+
}
44+
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
//// [tests/cases/conformance/declarationEmit/typeofImportTypeOnlyExport.ts] ////
2+
3+
//// [button.ts]
4+
import {classMap} from './lit.js';
5+
export const c = classMap();
6+
7+
//// [lit.ts]
8+
class ClassMapDirective {}
9+
10+
export type {ClassMapDirective};
11+
12+
export const directive =
13+
<C>(class_: C) =>
14+
() => ({
15+
directive: class_,
16+
});
17+
18+
export const classMap = directive(ClassMapDirective);
19+
20+
21+
//// [lit.js]
22+
"use strict";
23+
exports.__esModule = true;
24+
exports.classMap = exports.directive = void 0;
25+
var ClassMapDirective = /** @class */ (function () {
26+
function ClassMapDirective() {
27+
}
28+
return ClassMapDirective;
29+
}());
30+
var directive = function (class_) {
31+
return function () { return ({
32+
directive: class_
33+
}); };
34+
};
35+
exports.directive = directive;
36+
exports.classMap = (0, exports.directive)(ClassMapDirective);
37+
//// [button.js]
38+
"use strict";
39+
exports.__esModule = true;
40+
exports.c = void 0;
41+
var lit_js_1 = require("./lit.js");
42+
exports.c = (0, lit_js_1.classMap)();
43+
44+
45+
//// [lit.d.ts]
46+
declare class ClassMapDirective {
47+
}
48+
export type { ClassMapDirective };
49+
export declare const directive: <C>(class_: C) => () => {
50+
directive: C;
51+
};
52+
export declare const classMap: () => {
53+
directive: typeof ClassMapDirective;
54+
};
55+
//// [button.d.ts]
56+
export declare const c: {
57+
directive: typeof import("./lit.js").ClassMapDirective;
58+
};
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
=== tests/cases/conformance/declarationEmit/button.ts ===
2+
import {classMap} from './lit.js';
3+
>classMap : Symbol(classMap, Decl(button.ts, 0, 8))
4+
5+
export const c = classMap();
6+
>c : Symbol(c, Decl(button.ts, 1, 12))
7+
>classMap : Symbol(classMap, Decl(button.ts, 0, 8))
8+
9+
=== tests/cases/conformance/declarationEmit/lit.ts ===
10+
class ClassMapDirective {}
11+
>ClassMapDirective : Symbol(ClassMapDirective, Decl(lit.ts, 0, 0))
12+
13+
export type {ClassMapDirective};
14+
>ClassMapDirective : Symbol(ClassMapDirective, Decl(lit.ts, 2, 13))
15+
16+
export const directive =
17+
>directive : Symbol(directive, Decl(lit.ts, 4, 12))
18+
19+
<C>(class_: C) =>
20+
>C : Symbol(C, Decl(lit.ts, 5, 3))
21+
>class_ : Symbol(class_, Decl(lit.ts, 5, 6))
22+
>C : Symbol(C, Decl(lit.ts, 5, 3))
23+
24+
() => ({
25+
directive: class_,
26+
>directive : Symbol(directive, Decl(lit.ts, 6, 10))
27+
>class_ : Symbol(class_, Decl(lit.ts, 5, 6))
28+
29+
});
30+
31+
export const classMap = directive(ClassMapDirective);
32+
>classMap : Symbol(classMap, Decl(lit.ts, 10, 12))
33+
>directive : Symbol(directive, Decl(lit.ts, 4, 12))
34+
>ClassMapDirective : Symbol(ClassMapDirective, Decl(lit.ts, 0, 0))
35+
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
=== tests/cases/conformance/declarationEmit/button.ts ===
2+
import {classMap} from './lit.js';
3+
>classMap : () => { directive: typeof import("tests/cases/conformance/declarationEmit/lit").ClassMapDirective; }
4+
5+
export const c = classMap();
6+
>c : { directive: typeof import("tests/cases/conformance/declarationEmit/lit").ClassMapDirective; }
7+
>classMap() : { directive: typeof import("tests/cases/conformance/declarationEmit/lit").ClassMapDirective; }
8+
>classMap : () => { directive: typeof import("tests/cases/conformance/declarationEmit/lit").ClassMapDirective; }
9+
10+
=== tests/cases/conformance/declarationEmit/lit.ts ===
11+
class ClassMapDirective {}
12+
>ClassMapDirective : ClassMapDirective
13+
14+
export type {ClassMapDirective};
15+
>ClassMapDirective : ClassMapDirective
16+
17+
export const directive =
18+
>directive : <C>(class_: C) => () => { directive: C; }
19+
20+
<C>(class_: C) =>
21+
><C>(class_: C) => () => ({ directive: class_, }) : <C>(class_: C) => () => { directive: C; }
22+
>class_ : C
23+
24+
() => ({
25+
>() => ({ directive: class_, }) : () => { directive: C; }
26+
>({ directive: class_, }) : { directive: C; }
27+
>{ directive: class_, } : { directive: C; }
28+
29+
directive: class_,
30+
>directive : C
31+
>class_ : C
32+
33+
});
34+
35+
export const classMap = directive(ClassMapDirective);
36+
>classMap : () => { directive: typeof ClassMapDirective; }
37+
>directive(ClassMapDirective) : () => { directive: typeof ClassMapDirective; }
38+
>directive : <C>(class_: C) => () => { directive: C; }
39+
>ClassMapDirective : typeof ClassMapDirective
40+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// @declaration: true
2+
3+
// @Filename: button.ts
4+
import {classMap} from './lit.js';
5+
export const c = classMap();
6+
7+
// @Filename: lit.ts
8+
class ClassMapDirective {}
9+
10+
export type {ClassMapDirective};
11+
12+
export const directive =
13+
<C>(class_: C) =>
14+
() => ({
15+
directive: class_,
16+
});
17+
18+
export const classMap = directive(ClassMapDirective);

tests/cases/conformance/externalModules/typeOnly/namespaceImportTypeQuery.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,10 @@ export class B {};
77
import * as types from './a';
88
let A: typeof types.A;
99
let B: typeof types.B;
10+
11+
let t: typeof types = {
12+
// error: while you can ask for `typeof types.A`,
13+
// `typeof types` does not include `A`
14+
A: undefined as any,
15+
B: undefined as any,
16+
}

0 commit comments

Comments
 (0)