Support export type * (#52217)

This commit is contained in:
Andrew Branch
2023-01-20 14:24:48 -08:00
committed by GitHub
parent 2acbceed4b
commit f576398194
48 changed files with 1520 additions and 73 deletions
+60 -20
View File
@@ -3443,7 +3443,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (result && errorLocation && meaning & SymbolFlags.Value && result.flags & SymbolFlags.Alias && !(result.flags & SymbolFlags.Value) && !isValidTypeOnlyAliasUseSite(errorLocation)) {
const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(result, SymbolFlags.Value);
if (typeOnlyDeclaration) {
const message = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier
const message = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier || typeOnlyDeclaration.kind === SyntaxKind.ExportDeclaration || typeOnlyDeclaration.kind === SyntaxKind.NamespaceExport
? Diagnostics._0_cannot_be_used_as_a_value_because_it_was_exported_using_export_type
: Diagnostics._0_cannot_be_used_as_a_value_because_it_was_imported_using_import_type;
const unescapedName = unescapeLeadingUnderscores(name);
@@ -3464,7 +3464,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
diagnostic,
createDiagnosticForNode(
typeOnlyDeclaration,
typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier ? Diagnostics._0_was_exported_here : Diagnostics._0_was_imported_here,
typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier || typeOnlyDeclaration.kind === SyntaxKind.ExportDeclaration || typeOnlyDeclaration.kind === SyntaxKind.NamespaceExport
? Diagnostics._0_was_exported_here
: Diagnostics._0_was_imported_here,
unescapedName));
}
@@ -3861,7 +3863,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function checkAndReportErrorForResolvingImportAliasToTypeOnlySymbol(node: ImportEqualsDeclaration, resolved: Symbol | undefined) {
if (markSymbolOfAliasDeclarationIfTypeOnly(node, /*immediateTarget*/ undefined, resolved, /*overwriteEmpty*/ false) && !node.isTypeOnly) {
const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(getSymbolOfDeclaration(node))!;
const isExport = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier;
const isExport = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier || typeOnlyDeclaration.kind === SyntaxKind.ExportDeclaration;
const message = isExport
? Diagnostics.An_import_alias_cannot_reference_a_declaration_that_was_exported_using_export_type
: Diagnostics.An_import_alias_cannot_reference_a_declaration_that_was_imported_using_import_type;
@@ -3869,7 +3871,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
? Diagnostics._0_was_exported_here
: Diagnostics._0_was_imported_here;
const name = unescapeLeadingUnderscores(typeOnlyDeclaration.name.escapedText);
// TODO: how to get name for export *?
const name = typeOnlyDeclaration.kind === SyntaxKind.ExportDeclaration ? "*" : unescapeLeadingUnderscores(typeOnlyDeclaration.name.escapedText);
addRelatedInfo(error(node.moduleReference, message), createDiagnosticForNode(typeOnlyDeclaration, relatedMessage, name));
}
}
@@ -4083,7 +4086,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (symbol.flags & SymbolFlags.Module) {
const exportSymbol = getExportsOfSymbol(symbol).get(name.escapedText);
const resolved = resolveSymbol(exportSymbol, dontResolveAlias);
markSymbolOfAliasDeclarationIfTypeOnly(specifier, exportSymbol, resolved, /*overwriteEmpty*/ false);
const exportStarDeclaration = getSymbolLinks(symbol).typeOnlyExportStarMap?.get(name.escapedText);
markSymbolOfAliasDeclarationIfTypeOnly(specifier, exportSymbol, resolved, /*overwriteEmpty*/ false, exportStarDeclaration, name.escapedText);
return resolved;
}
}
@@ -4444,6 +4448,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
immediateTarget: Symbol | undefined,
finalTarget: Symbol | undefined,
overwriteEmpty: boolean,
exportStarDeclaration?: ExportDeclaration & { readonly isTypeOnly: true },
exportStarName?: __String,
): boolean {
if (!aliasDeclaration || isPropertyAccessExpression(aliasDeclaration)) return false;
@@ -4455,6 +4461,14 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
links.typeOnlyDeclaration = aliasDeclaration;
return true;
}
if (exportStarDeclaration) {
const links = getSymbolLinks(sourceSymbol);
links.typeOnlyDeclaration = exportStarDeclaration;
if (sourceSymbol.escapedName !== exportStarName) {
links.typeOnlyExportStarName = exportStarName;
}
return true;
}
const links = getSymbolLinks(sourceSymbol);
return markSymbolOfAliasDeclarationIfTypeOnlyWorker(links, immediateTarget, overwriteEmpty)
@@ -4480,7 +4494,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
return links.typeOnlyDeclaration || undefined;
}
if (links.typeOnlyDeclaration) {
return getAllSymbolFlags(resolveAlias(links.typeOnlyDeclaration.symbol)) & include ? links.typeOnlyDeclaration : undefined;
const resolved = links.typeOnlyDeclaration.kind === SyntaxKind.ExportDeclaration
? resolveSymbol(getExportsOfModule(links.typeOnlyDeclaration.symbol.parent!).get(links.typeOnlyExportStarName || symbol.escapedName))!
: resolveAlias(links.typeOnlyDeclaration.symbol);
return getAllSymbolFlags(resolved) & include ? links.typeOnlyDeclaration : undefined;
}
return undefined;
}
@@ -5203,7 +5220,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
function getExportsOfModule(moduleSymbol: Symbol): SymbolTable {
const links = getSymbolLinks(moduleSymbol);
return links.resolvedExports || (links.resolvedExports = getExportsOfModuleWorker(moduleSymbol));
if (!links.resolvedExports) {
const { exports, typeOnlyExportStarMap } = getExportsOfModuleWorker(moduleSymbol);
links.resolvedExports = exports;
links.typeOnlyExportStarMap = typeOnlyExportStarMap;
}
return links.resolvedExports;
}
interface ExportCollisionTracker {
@@ -5243,21 +5265,38 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
});
}
function getExportsOfModuleWorker(moduleSymbol: Symbol): SymbolTable {
function getExportsOfModuleWorker(moduleSymbol: Symbol) {
const visitedSymbols: Symbol[] = [];
let typeOnlyExportStarMap: UnderscoreEscapedMap<ExportDeclaration & { readonly isTypeOnly: true }> | undefined;
const nonTypeOnlyNames = new Set<__String>();
// A module defined by an 'export=' consists of one export that needs to be resolved
moduleSymbol = resolveExternalModuleSymbol(moduleSymbol);
const exports = visit(moduleSymbol) || emptySymbols;
return visit(moduleSymbol) || emptySymbols;
if (typeOnlyExportStarMap) {
nonTypeOnlyNames.forEach(name => typeOnlyExportStarMap!.delete(name));
}
return {
exports,
typeOnlyExportStarMap,
};
// The ES6 spec permits export * declarations in a module to circularly reference the module itself. For example,
// module 'a' can 'export * from "b"' and 'b' can 'export * from "a"' without error.
function visit(symbol: Symbol | undefined): SymbolTable | undefined {
function visit(symbol: Symbol | undefined, exportStar?: ExportDeclaration, isTypeOnly?: boolean): SymbolTable | undefined {
if (!isTypeOnly && symbol?.exports) {
// Add non-type-only names before checking if we've visited this module,
// because we might have visited it via an 'export type *', and visiting
// again with 'export *' will override the type-onlyness of its exports.
symbol.exports.forEach((_, name) => nonTypeOnlyNames.add(name));
}
if (!(symbol && symbol.exports && pushIfUnique(visitedSymbols, symbol))) {
return;
}
const symbols = new Map(symbol.exports);
// All export * declarations are collected in an __export symbol by the binder
const exportStars = symbol.exports.get(InternalSymbolName.ExportStar);
if (exportStars) {
@@ -5266,7 +5305,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (exportStars.declarations) {
for (const node of exportStars.declarations) {
const resolvedModule = resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier!);
const exportedSymbols = visit(resolvedModule);
const exportedSymbols = visit(resolvedModule, node as ExportDeclaration, isTypeOnly || (node as ExportDeclaration).isTypeOnly);
extendExportSymbols(
nestedSymbols,
exportedSymbols,
@@ -5291,6 +5330,12 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
});
extendExportSymbols(symbols, nestedSymbols);
}
if (exportStar?.isTypeOnly) {
typeOnlyExportStarMap ??= new Map();
symbols.forEach((_, escapedName) => typeOnlyExportStarMap!.set(
escapedName,
exportStar as ExportDeclaration & { readonly isTypeOnly: true }));
}
return symbols;
}
}
@@ -8597,7 +8642,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
for (const node of symbol.declarations) {
const resolvedModule = resolveExternalModuleName(node, (node as ExportDeclaration).moduleSpecifier!);
if (!resolvedModule) continue;
addResult(factory.createExportDeclaration(/*modifiers*/ undefined, /*isTypeOnly*/ false, /*exportClause*/ undefined, factory.createStringLiteral(getSpecifierForModuleSymbol(resolvedModule, context))), ModifierFlags.None);
addResult(factory.createExportDeclaration(/*modifiers*/ undefined, /*isTypeOnly*/ (node as ExportDeclaration).isTypeOnly, /*exportClause*/ undefined, factory.createStringLiteral(getSpecifierForModuleSymbol(resolvedModule, context))), ModifierFlags.None);
}
}
}
@@ -12219,7 +12264,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
if (!links[resolutionKind]) {
const isStatic = resolutionKind === MembersOrExportsResolutionKind.resolvedExports;
const earlySymbols = !isStatic ? symbol.members :
symbol.flags & SymbolFlags.Module ? getExportsOfModuleWorker(symbol) :
symbol.flags & SymbolFlags.Module ? getExportsOfModuleWorker(symbol).exports :
symbol.exports;
// In the event we recursively resolve the members/exports of the symbol, we
@@ -43738,13 +43783,8 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
}
function checkGrammarExportDeclaration(node: ExportDeclaration): boolean {
if (node.isTypeOnly) {
if (node.exportClause?.kind === SyntaxKind.NamedExports) {
return checkGrammarNamedImportsOrExports(node.exportClause);
}
else {
return grammarErrorOnNode(node, Diagnostics.Only_named_exports_may_use_export_type);
}
if (node.isTypeOnly && node.exportClause?.kind === SyntaxKind.NamedExports) {
return checkGrammarNamedImportsOrExports(node.exportClause);
}
return false;
}
-4
View File
@@ -1232,10 +1232,6 @@
"category": "Error",
"code": 1382
},
"Only named exports may use 'export type'.": {
"category": "Error",
"code": 1383
},
"Function type notation must be parenthesized when used in a union type.": {
"category": "Error",
"code": 1385
+13 -2
View File
@@ -3788,16 +3788,25 @@ export type TypeOnlyCompatibleAliasDeclaration =
| ImportEqualsDeclaration
| NamespaceImport
| ImportOrExportSpecifier
| ExportDeclaration
| NamespaceExport
;
export type TypeOnlyAliasDeclaration =
export type TypeOnlyImportDeclaration =
| ImportClause & { readonly isTypeOnly: true, readonly name: Identifier }
| ImportEqualsDeclaration & { readonly isTypeOnly: true }
| NamespaceImport & { readonly parent: ImportClause & { readonly isTypeOnly: true } }
| ImportSpecifier & ({ readonly isTypeOnly: true } | { readonly parent: NamedImports & { readonly parent: ImportClause & { readonly isTypeOnly: true } } })
| ExportSpecifier & ({ readonly isTypeOnly: true } | { readonly parent: NamedExports & { readonly parent: ExportDeclaration & { readonly isTypeOnly: true } } })
;
export type TypeOnlyExportDeclaration =
| ExportSpecifier & ({ readonly isTypeOnly: true } | { readonly parent: NamedExports & { readonly parent: ExportDeclaration & { readonly isTypeOnly: true } } })
| ExportDeclaration & { readonly isTypeOnly: true } // export * from "mod"
| NamespaceExport & { readonly parent: ExportDeclaration & { readonly isTypeOnly: true } } // export * as ns from "mod"
;
export type TypeOnlyAliasDeclaration = TypeOnlyImportDeclaration | TypeOnlyExportDeclaration;
/**
* This is either an `export =` or an `export default` declaration.
* Unless `isExportEquals` is set, this node was parsed as an `export default`.
@@ -5792,6 +5801,8 @@ export interface SymbolLinks {
deferralParent?: Type; // Source union/intersection of a deferred type
cjsExportMerged?: Symbol; // Version of the symbol with all non export= exports merged with the export= target
typeOnlyDeclaration?: TypeOnlyAliasDeclaration | false; // First resolved alias declaration that makes the symbol only usable in type constructs
typeOnlyExportStarMap?: UnderscoreEscapedMap<ExportDeclaration & { readonly isTypeOnly: true }>; // Set on a module symbol when some of its exports were resolved through a 'export type * from "mod"' declaration
typeOnlyExportStarName?: __String; // Set to the name of the symbol re-exported by an 'export type *' declaration, when different from the symbol name
isConstructorDeclaredProperty?: boolean; // Property declared through 'this.x = ...' assignment in constructor
tupleLabelDeclaration?: NamedTupleMember | ParameterDeclaration; // Declaration associated with the tuple's label
accessibleChainCache?: Map<string, Symbol[] | undefined>;
+23 -6
View File
@@ -55,6 +55,7 @@ import {
EnumDeclaration,
every,
ExportAssignment,
ExportDeclaration,
ExportSpecifier,
Expression,
FileReference,
@@ -92,7 +93,6 @@ import {
Identifier,
ImportClause,
ImportEqualsDeclaration,
ImportOrExportSpecifier,
ImportSpecifier,
ImportTypeNode,
isAccessExpression,
@@ -214,6 +214,7 @@ import {
NamedExportBindings,
NamedImportBindings,
NamespaceBody,
NamespaceExport,
NamespaceImport,
NewExpression,
Node,
@@ -270,6 +271,8 @@ import {
TypeElement,
TypeNode,
TypeOnlyAliasDeclaration,
TypeOnlyExportDeclaration,
TypeOnlyImportDeclaration,
TypeParameterDeclaration,
TypeReferenceType,
UnaryExpression,
@@ -1480,19 +1483,33 @@ export function isImportOrExportSpecifier(node: Node): node is ImportSpecifier |
return isImportSpecifier(node) || isExportSpecifier(node);
}
export function isTypeOnlyImportOrExportDeclaration(node: Node): node is TypeOnlyAliasDeclaration {
export function isTypeOnlyImportDeclaration(node: Node): node is TypeOnlyImportDeclaration {
switch (node.kind) {
case SyntaxKind.ImportSpecifier:
case SyntaxKind.ExportSpecifier:
return (node as ImportOrExportSpecifier).isTypeOnly || (node as ImportOrExportSpecifier).parent.parent.isTypeOnly;
return (node as ImportSpecifier).isTypeOnly || (node as ImportSpecifier).parent.parent.isTypeOnly;
case SyntaxKind.NamespaceImport:
return (node as NamespaceImport).parent.isTypeOnly;
case SyntaxKind.ImportClause:
case SyntaxKind.ImportEqualsDeclaration:
return (node as ImportClause | ImportEqualsDeclaration).isTypeOnly;
default:
return false;
}
return false;
}
export function isTypeOnlyExportDeclaration(node: Node): node is TypeOnlyExportDeclaration {
switch (node.kind) {
case SyntaxKind.ExportSpecifier:
return (node as ExportSpecifier).isTypeOnly || (node as ExportSpecifier).parent.parent.isTypeOnly;
case SyntaxKind.ExportDeclaration:
return (node as ExportDeclaration).isTypeOnly && !!(node as ExportDeclaration).moduleSpecifier && !(node as ExportDeclaration).exportClause;
case SyntaxKind.NamespaceExport:
return (node as NamespaceExport).parent.isTypeOnly;
}
return false;
}
export function isTypeOnlyImportOrExportDeclaration(node: Node): node is TypeOnlyAliasDeclaration {
return isTypeOnlyImportDeclaration(node) || isTypeOnlyExportDeclaration(node);
}
export function isAssertionKey(node: Node): node is AssertionKey {
+7 -6
View File
@@ -227,6 +227,7 @@ import {
isTypeLiteralNode,
isTypeNode,
isTypeOfExpression,
isTypeOnlyImportDeclaration,
isTypeOnlyImportOrExportDeclaration,
isTypeReferenceType,
isValidTypeOnlyAliasUseSite,
@@ -352,7 +353,7 @@ import {
typeHasCallOrConstructSignatures,
TypeLiteralNode,
TypeNode,
TypeOnlyAliasDeclaration,
TypeOnlyImportDeclaration,
TypeQueryNode,
TypeReferenceNode,
unescapeLeadingUnderscores,
@@ -467,8 +468,8 @@ interface SymbolOriginInfoResolvedExport extends SymbolOriginInfo {
moduleSpecifier: string;
}
interface SymbolOriginInfoTypeOnlyAlias extends SymbolOriginInfo {
declaration: TypeOnlyAliasDeclaration;
interface SymbolOriginInfoTypeOnlyImport extends SymbolOriginInfo {
declaration: TypeOnlyImportDeclaration;
}
interface SymbolOriginInfoObjectLiteralMethod extends SymbolOriginInfo {
@@ -509,7 +510,7 @@ function originIsNullableMember(origin: SymbolOriginInfo): boolean {
return !!(origin.kind & SymbolOriginInfoKind.Nullable);
}
function originIsTypeOnlyAlias(origin: SymbolOriginInfo | undefined): origin is SymbolOriginInfoTypeOnlyAlias {
function originIsTypeOnlyAlias(origin: SymbolOriginInfo | undefined): origin is SymbolOriginInfoTypeOnlyImport {
return !!(origin && origin.kind & SymbolOriginInfoKind.TypeOnlyAlias);
}
@@ -3351,9 +3352,9 @@ function getCompletionData(
symbolToSortTextMap[getSymbolId(symbol)] = SortText.GlobalsOrKeywords;
}
if (typeOnlyAliasNeedsPromotion && !(symbol.flags & SymbolFlags.Value)) {
const typeOnlyAliasDeclaration = symbol.declarations && find(symbol.declarations, isTypeOnlyImportOrExportDeclaration);
const typeOnlyAliasDeclaration = symbol.declarations && find(symbol.declarations, isTypeOnlyImportDeclaration);
if (typeOnlyAliasDeclaration) {
const origin: SymbolOriginInfoTypeOnlyAlias = { kind: SymbolOriginInfoKind.TypeOnlyAlias, declaration: typeOnlyAliasDeclaration };
const origin: SymbolOriginInfoTypeOnlyImport = { kind: SymbolOriginInfoKind.TypeOnlyAlias, declaration: typeOnlyAliasDeclaration };
symbolToOriginInfoMap[i] = origin;
}
}