Allow referencing type-only exports as namespace members in ImportTypes and TypeQueries (#49056)

* Allow referencing type-only exports as namespace members in ImportTypes and TypeQueries

* Add extra test case

* ;; -> ;

* undefined -> false

Co-authored-by: Daniel Rosenwasser <DanielRosenwasser@users.noreply.github.com>
This commit is contained in:
Andrew Branch
2022-05-11 11:44:59 -07:00
committed by GitHub
parent 3c87170604
commit 5c2febfe02
10 changed files with 230 additions and 13 deletions

View File

@@ -4110,8 +4110,10 @@ namespace ts {
return getMergedSymbol(symbol && (symbol.flags & SymbolFlags.ExportValue) !== 0 && symbol.exportSymbol || symbol);
}
function symbolIsValue(symbol: Symbol): boolean {
return !!(symbol.flags & SymbolFlags.Value || symbol.flags & SymbolFlags.Alias && resolveAlias(symbol).flags & SymbolFlags.Value && !getTypeOnlyAliasDeclaration(symbol));
function symbolIsValue(symbol: Symbol, includeTypeOnlyMembers?: boolean): boolean {
return !!(
symbol.flags & SymbolFlags.Value ||
symbol.flags & SymbolFlags.Alias && resolveAlias(symbol).flags & SymbolFlags.Value && (includeTypeOnlyMembers || !getTypeOnlyAliasDeclaration(symbol)));
}
function findConstructorDeclaration(node: ClassLikeDeclaration): ConstructorDeclaration | undefined {
@@ -12575,12 +12577,12 @@ 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, skipObjectFunctionPropertyAugment?: boolean): Symbol | undefined {
function getPropertyOfType(type: Type, name: __String, skipObjectFunctionPropertyAugment?: boolean, includeTypeOnlyMembers?: boolean): Symbol | undefined {
type = getReducedApparentType(type);
if (type.flags & TypeFlags.Object) {
const resolved = resolveStructuredTypeMembers(type as ObjectType);
const symbol = resolved.members.get(name);
if (symbol && symbolIsValue(symbol)) {
if (symbol && symbolIsValue(symbol, includeTypeOnlyMembers)) {
return symbol;
}
if (skipObjectFunctionPropertyAugment) return undefined;
@@ -16208,7 +16210,7 @@ namespace ts {
// the `exports` lookup process that only looks up namespace members which is used for most type references
const mergedResolvedSymbol = getMergedSymbol(resolveSymbol(currentNamespace));
const next = node.isTypeOf
? getPropertyOfType(getTypeOfSymbol(mergedResolvedSymbol), current.escapedText)
? getPropertyOfType(getTypeOfSymbol(mergedResolvedSymbol), current.escapedText, /*skipObjectFunctionPropertyAugment*/ false, /*includeTypeOnlyMembers*/ true)
: getSymbol(getExportsOfSymbol(mergedResolvedSymbol), current.escapedText, meaning);
if (!next) {
error(current, Diagnostics.Namespace_0_has_no_exported_member_1, getFullyQualifiedName(currentNamespace), declarationNameToString(current));
@@ -28995,9 +28997,9 @@ namespace ts {
if (isIdentifier(left) && parentSymbol) {
markAliasReferenced(parentSymbol, node);
}
return isErrorType(apparentType) ? errorType : apparentType;;
return isErrorType(apparentType) ? errorType : apparentType;
}
prop = getPropertyOfType(apparentType, right.escapedText);
prop = getPropertyOfType(apparentType, right.escapedText, /*skipObjectFunctionPropertyAugment*/ false, /*includeTypeOnlyMembers*/ node.kind === SyntaxKind.QualifiedName);
}
// In `Foo.Bar.Baz`, 'Foo' is not referenced if 'Bar' is a const enum or a module containing only const enums.
// `Foo` is also not referenced in `enum FooCopy { Bar = Foo.Bar }`, because the enum member value gets inlined

View File

@@ -1,4 +1,5 @@
/b.ts(2,21): error TS2339: Property 'A' does not exist on type 'typeof import("/a")'.
/b.ts(8,3): error TS2322: Type '{ A: any; B: any; }' is not assignable to type 'typeof import("/a")'.
Object literal may only specify known properties, and 'A' does not exist in type 'typeof import("/a")'.
==== /a.ts (0 errors) ====
@@ -9,7 +10,15 @@
==== /b.ts (1 errors) ====
import * as types from './a';
let A: typeof types.A;
~
!!! error TS2339: Property 'A' does not exist on type 'typeof import("/a")'.
let B: typeof types.B;
let t: typeof types = {
// error: while you can ask for `typeof types.A`,
// `typeof types` does not include `A`
A: undefined as any,
~~~~~~~~~~~~~~~~~~~
!!! error TS2322: Type '{ A: any; B: any; }' is not assignable to type 'typeof import("/a")'.
!!! error TS2322: Object literal may only specify known properties, and 'A' does not exist in type 'typeof import("/a")'.
B: undefined as any,
}

View File

@@ -9,6 +9,13 @@ export class B {};
import * as types from './a';
let A: typeof types.A;
let B: typeof types.B;
let t: typeof types = {
// error: while you can ask for `typeof types.A`,
// `typeof types` does not include `A`
A: undefined as any,
B: undefined as any,
}
//// [a.js]
@@ -32,3 +39,9 @@ exports.B = B;
exports.__esModule = true;
var A;
var B;
var t = {
// error: while you can ask for `typeof types.A`,
// `typeof types` does not include `A`
A: undefined,
B: undefined
};

View File

@@ -14,7 +14,9 @@ import * as types from './a';
let A: typeof types.A;
>A : Symbol(A, Decl(b.ts, 1, 3))
>types.A : Symbol(types.A, Decl(a.ts, 1, 13))
>types : Symbol(types, Decl(b.ts, 0, 6))
>A : Symbol(types.A, Decl(a.ts, 1, 13))
let B: typeof types.B;
>B : Symbol(B, Decl(b.ts, 2, 3))
@@ -22,3 +24,18 @@ let B: typeof types.B;
>types : Symbol(types, Decl(b.ts, 0, 6))
>B : Symbol(types.B, Decl(a.ts, 1, 18))
let t: typeof types = {
>t : Symbol(t, Decl(b.ts, 4, 3))
>types : Symbol(types, Decl(b.ts, 0, 6))
// error: while you can ask for `typeof types.A`,
// `typeof types` does not include `A`
A: undefined as any,
>A : Symbol(A, Decl(b.ts, 4, 23))
>undefined : Symbol(undefined)
B: undefined as any,
>B : Symbol(B, Decl(b.ts, 7, 22))
>undefined : Symbol(undefined)
}

View File

@@ -13,10 +13,10 @@ import * as types from './a';
>types : typeof types
let A: typeof types.A;
>A : any
>types.A : any
>A : typeof types.A
>types.A : typeof types.A
>types : typeof types
>A : any
>A : typeof types.A
let B: typeof types.B;
>B : typeof types.B
@@ -24,3 +24,21 @@ let B: typeof types.B;
>types : typeof types
>B : typeof types.B
let t: typeof types = {
>t : typeof types
>types : typeof types
>{ // 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; }
// error: while you can ask for `typeof types.A`,
// `typeof types` does not include `A`
A: undefined as any,
>A : any
>undefined as any : any
>undefined : undefined
B: undefined as any,
>B : any
>undefined as any : any
>undefined : undefined
}

View File

@@ -0,0 +1,58 @@
//// [tests/cases/conformance/declarationEmit/typeofImportTypeOnlyExport.ts] ////
//// [button.ts]
import {classMap} from './lit.js';
export const c = classMap();
//// [lit.ts]
class ClassMapDirective {}
export type {ClassMapDirective};
export const directive =
<C>(class_: C) =>
() => ({
directive: class_,
});
export const classMap = directive(ClassMapDirective);
//// [lit.js]
"use strict";
exports.__esModule = true;
exports.classMap = exports.directive = void 0;
var ClassMapDirective = /** @class */ (function () {
function ClassMapDirective() {
}
return ClassMapDirective;
}());
var directive = function (class_) {
return function () { return ({
directive: class_
}); };
};
exports.directive = directive;
exports.classMap = (0, exports.directive)(ClassMapDirective);
//// [button.js]
"use strict";
exports.__esModule = true;
exports.c = void 0;
var lit_js_1 = require("./lit.js");
exports.c = (0, lit_js_1.classMap)();
//// [lit.d.ts]
declare class ClassMapDirective {
}
export type { ClassMapDirective };
export declare const directive: <C>(class_: C) => () => {
directive: C;
};
export declare const classMap: () => {
directive: typeof ClassMapDirective;
};
//// [button.d.ts]
export declare const c: {
directive: typeof import("./lit.js").ClassMapDirective;
};

View File

@@ -0,0 +1,35 @@
=== tests/cases/conformance/declarationEmit/button.ts ===
import {classMap} from './lit.js';
>classMap : Symbol(classMap, Decl(button.ts, 0, 8))
export const c = classMap();
>c : Symbol(c, Decl(button.ts, 1, 12))
>classMap : Symbol(classMap, Decl(button.ts, 0, 8))
=== tests/cases/conformance/declarationEmit/lit.ts ===
class ClassMapDirective {}
>ClassMapDirective : Symbol(ClassMapDirective, Decl(lit.ts, 0, 0))
export type {ClassMapDirective};
>ClassMapDirective : Symbol(ClassMapDirective, Decl(lit.ts, 2, 13))
export const directive =
>directive : Symbol(directive, Decl(lit.ts, 4, 12))
<C>(class_: C) =>
>C : Symbol(C, Decl(lit.ts, 5, 3))
>class_ : Symbol(class_, Decl(lit.ts, 5, 6))
>C : Symbol(C, Decl(lit.ts, 5, 3))
() => ({
directive: class_,
>directive : Symbol(directive, Decl(lit.ts, 6, 10))
>class_ : Symbol(class_, Decl(lit.ts, 5, 6))
});
export const classMap = directive(ClassMapDirective);
>classMap : Symbol(classMap, Decl(lit.ts, 10, 12))
>directive : Symbol(directive, Decl(lit.ts, 4, 12))
>ClassMapDirective : Symbol(ClassMapDirective, Decl(lit.ts, 0, 0))

View File

@@ -0,0 +1,40 @@
=== tests/cases/conformance/declarationEmit/button.ts ===
import {classMap} from './lit.js';
>classMap : () => { directive: typeof import("tests/cases/conformance/declarationEmit/lit").ClassMapDirective; }
export const c = classMap();
>c : { directive: typeof import("tests/cases/conformance/declarationEmit/lit").ClassMapDirective; }
>classMap() : { directive: typeof import("tests/cases/conformance/declarationEmit/lit").ClassMapDirective; }
>classMap : () => { directive: typeof import("tests/cases/conformance/declarationEmit/lit").ClassMapDirective; }
=== tests/cases/conformance/declarationEmit/lit.ts ===
class ClassMapDirective {}
>ClassMapDirective : ClassMapDirective
export type {ClassMapDirective};
>ClassMapDirective : ClassMapDirective
export const directive =
>directive : <C>(class_: C) => () => { directive: C; }
<C>(class_: C) =>
><C>(class_: C) => () => ({ directive: class_, }) : <C>(class_: C) => () => { directive: C; }
>class_ : C
() => ({
>() => ({ directive: class_, }) : () => { directive: C; }
>({ directive: class_, }) : { directive: C; }
>{ directive: class_, } : { directive: C; }
directive: class_,
>directive : C
>class_ : C
});
export const classMap = directive(ClassMapDirective);
>classMap : () => { directive: typeof ClassMapDirective; }
>directive(ClassMapDirective) : () => { directive: typeof ClassMapDirective; }
>directive : <C>(class_: C) => () => { directive: C; }
>ClassMapDirective : typeof ClassMapDirective

View File

@@ -0,0 +1,18 @@
// @declaration: true
// @Filename: button.ts
import {classMap} from './lit.js';
export const c = classMap();
// @Filename: lit.ts
class ClassMapDirective {}
export type {ClassMapDirective};
export const directive =
<C>(class_: C) =>
() => ({
directive: class_,
});
export const classMap = directive(ClassMapDirective);

View File

@@ -7,3 +7,10 @@ export class B {};
import * as types from './a';
let A: typeof types.A;
let B: typeof types.B;
let t: typeof types = {
// error: while you can ask for `typeof types.A`,
// `typeof types` does not include `A`
A: undefined as any,
B: undefined as any,
}