Update type-only import semantics to allow type queries (#36092)

* Change type-only semantics to allow type queries

* Don’t error using type-only import in ambient context

* Fix default import

* Fix namespace import

* Update more baselines

* Prevent circular resolution

* Track const enum expression usage

* Update baselines

* Perf tuning 1

* Test commit for perf impact

* Weave type-only alias declaration finding into alias resolution

* Fix namespace import of type-only exported symbols

* type-only exports do not contribute to the module object type

* Update APIs

* Fix enum casing, remove type-only conversion suggestion

* Short circuit type-only checks in resolveEntityName faster

* Fix casing in API

* Remove unused parameter

* Fix error on qualified names in type queries

* Allow type-only imports in computed property names

* Fix computed property names of types and abstract members

* Remove unused util

* Commit missing baselines

* Rename “check” functions so as not to overload the word “check”
This commit is contained in:
Andrew Branch
2020-01-23 12:53:36 -08:00
committed by GitHub
parent 0276e7f910
commit b05dde747c
76 changed files with 1406 additions and 326 deletions
+131 -156
View File
@@ -1861,10 +1861,36 @@ namespace ts {
error(errorLocation, Diagnostics.Initializer_of_parameter_0_cannot_reference_identifier_1_declared_after_it, declarationNameToString(associatedDeclarationForContainingInitializer.name), declarationNameToString(<Identifier>errorLocation));
}
}
if (result && errorLocation && meaning & SymbolFlags.Value && result.flags & SymbolFlags.Alias) {
checkSymbolUsageInExpressionContext(result, name, errorLocation);
}
}
return result;
}
function checkSymbolUsageInExpressionContext(symbol: Symbol, name: __String, useSite: Node) {
if (
!(useSite.flags & NodeFlags.Ambient) &&
!isPartOfTypeQuery(useSite) &&
!isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(useSite) &&
isExpressionNode(useSite)
) {
const typeOnlyDeclaration = getTypeOnlyAliasDeclaration(symbol);
if (typeOnlyDeclaration) {
const message = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier
? 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 relatedMessage = typeOnlyDeclaration.kind === SyntaxKind.ExportSpecifier
? Diagnostics._0_was_exported_here
: Diagnostics._0_was_imported_here;
const unescapedName = unescapeLeadingUnderscores(name);
addRelatedInfo(
error(useSite, message, unescapedName),
createDiagnosticForNode(typeOnlyDeclaration, relatedMessage, unescapedName));
}
}
}
function getIsDeferredContext(location: Node, lastLocation: Node | undefined): boolean {
if (location.kind !== SyntaxKind.ArrowFunction && location.kind !== SyntaxKind.FunctionExpression) {
// initializers in instance property declaration of class like entities are executed in constructor and thus deferred
@@ -2055,9 +2081,7 @@ namespace ts {
if (symbol) {
error(
errorLocation,
isTypeOnlyEnumAlias(symbol)
? Diagnostics.Enum_0_cannot_be_used_as_a_value_because_only_its_type_has_been_imported
: Diagnostics.Cannot_use_namespace_0_as_a_value,
Diagnostics.Cannot_use_namespace_0_as_a_value,
unescapeLeadingUnderscores(name));
return true;
}
@@ -2146,11 +2170,14 @@ namespace ts {
return getSymbolOfPartOfRightHandSideOfImportEquals(node.moduleReference, dontResolveAlias);
}
function resolveExportByName(moduleSymbol: Symbol, name: __String, dontResolveAlias: boolean) {
function resolveExportByName(moduleSymbol: Symbol, name: __String, sourceNode: TypeOnlyCompatibleAliasDeclaration | undefined, dontResolveAlias: boolean) {
const exportValue = moduleSymbol.exports!.get(InternalSymbolName.ExportEquals);
return exportValue
? getPropertyOfType(getTypeOfSymbol(exportValue), name)
: resolveSymbol(moduleSymbol.exports!.get(name), dontResolveAlias);
if (exportValue) {
return getPropertyOfType(getTypeOfSymbol(exportValue), name);
}
const exportSymbol = moduleSymbol.exports!.get(name);
markSymbolOfAliasDeclarationIfResolvesToTypeOnly(sourceNode, exportSymbol);
return resolveSymbol(exportSymbol, dontResolveAlias);
}
function isSyntacticDefault(node: Node) {
@@ -2164,13 +2191,13 @@ namespace ts {
// Declaration files (and ambient modules)
if (!file || file.isDeclarationFile) {
// Definitely cannot have a synthetic default if they have a syntactic default member specified
const defaultExportSymbol = resolveExportByName(moduleSymbol, InternalSymbolName.Default, /*dontResolveAlias*/ true); // Dont resolve alias because we want the immediately exported symbol's declaration
const defaultExportSymbol = resolveExportByName(moduleSymbol, InternalSymbolName.Default, /*sourceNode*/ undefined, /*dontResolveAlias*/ true); // Dont resolve alias because we want the immediately exported symbol's declaration
if (defaultExportSymbol && some(defaultExportSymbol.declarations, isSyntacticDefault)) {
return false;
}
// It _might_ still be incorrect to assume there is no __esModule marker on the import at runtime, even if there is no `default` member
// So we check a bit more,
if (resolveExportByName(moduleSymbol, escapeLeadingUnderscores("__esModule"), dontResolveAlias)) {
if (resolveExportByName(moduleSymbol, escapeLeadingUnderscores("__esModule"), /*sourceNode*/ undefined, dontResolveAlias)) {
// If there is an `__esModule` specified in the declaration (meaning someone explicitly added it or wrote it in their code),
// it definitely is a module and does not have a synthetic default
return false;
@@ -2185,11 +2212,12 @@ namespace ts {
return hasExportAssignmentSymbol(moduleSymbol);
}
// JS files have a synthetic default if they do not contain ES2015+ module syntax (export = is not valid in js) _and_ do not have an __esModule marker
return !file.externalModuleIndicator && !resolveExportByName(moduleSymbol, escapeLeadingUnderscores("__esModule"), dontResolveAlias);
return !file.externalModuleIndicator && !resolveExportByName(moduleSymbol, escapeLeadingUnderscores("__esModule"), /*sourceNode*/ undefined, dontResolveAlias);
}
function getTargetOfImportClause(node: ImportClause, dontResolveAlias: boolean): Symbol | undefined {
const moduleSymbol = resolveExternalModuleName(node, node.parent.moduleSpecifier);
markSymbolOfAliasDeclarationIfTypeOnly(node);
if (moduleSymbol) {
let exportDefaultSymbol: Symbol | undefined;
@@ -2197,7 +2225,7 @@ namespace ts {
exportDefaultSymbol = moduleSymbol;
}
else {
exportDefaultSymbol = resolveExportByName(moduleSymbol, InternalSymbolName.Default, dontResolveAlias);
exportDefaultSymbol = resolveExportByName(moduleSymbol, InternalSymbolName.Default, node, dontResolveAlias);
}
const file = find(moduleSymbol.declarations, isSourceFile);
@@ -2232,25 +2260,16 @@ namespace ts {
}
else if (hasSyntheticDefault) {
// per emit behavior, a synthetic default overrides a "real" .default member if `__esModule` is not present
return maybeTypeOnly(
resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) ||
resolveSymbol(moduleSymbol, dontResolveAlias));
return resolveExternalModuleSymbol(moduleSymbol, dontResolveAlias) || resolveSymbol(moduleSymbol, dontResolveAlias);
}
return maybeTypeOnly(exportDefaultSymbol);
}
function maybeTypeOnly(symbol: Symbol | undefined) {
if (symbol && node.isTypeOnly && node.name) {
return createTypeOnlyImportOrExport(node.name, symbol);
}
return symbol;
return exportDefaultSymbol;
}
}
function getTargetOfNamespaceImport(node: NamespaceImport, dontResolveAlias: boolean): Symbol | undefined {
const moduleSpecifier = node.parent.parent.moduleSpecifier;
const moduleSymbol = resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier, dontResolveAlias, /*suppressUsageError*/ false);
return moduleSymbol && node.parent.isTypeOnly ? createTypeOnlySymbol(moduleSymbol) : moduleSymbol;
markSymbolOfAliasDeclarationIfTypeOnly(node);
return resolveESModuleSymbol(resolveExternalModuleName(node, moduleSpecifier), moduleSpecifier, dontResolveAlias, /*suppressUsageError*/ false);
}
function getTargetOfNamespaceExport(node: NamespaceExport, dontResolveAlias: boolean): Symbol | undefined {
@@ -2292,9 +2311,12 @@ namespace ts {
return result;
}
function getExportOfModule(symbol: Symbol, name: __String, dontResolveAlias: boolean): Symbol | undefined {
function getExportOfModule(symbol: Symbol, specifier: ImportOrExportSpecifier, dontResolveAlias: boolean): Symbol | undefined {
if (symbol.flags & SymbolFlags.Module) {
return resolveSymbol(getExportsOfSymbol(symbol).get(name)!, dontResolveAlias);
const name = (specifier.propertyName ?? specifier.name).escapedText;
const exportSymbol = getExportsOfSymbol(symbol).get(name);
markSymbolOfAliasDeclarationIfResolvesToTypeOnly(specifier, exportSymbol);
return resolveSymbol(exportSymbol, dontResolveAlias);
}
}
@@ -2329,8 +2351,7 @@ namespace ts {
// if symbolFromVariable is export - get its final target
symbolFromVariable = resolveSymbol(symbolFromVariable, dontResolveAlias);
let symbolFromModule = getExportOfModule(targetSymbol, name.escapedText, dontResolveAlias);
let symbolFromModule = getExportOfModule(targetSymbol, specifier, dontResolveAlias);
if (symbolFromModule === undefined && name.escapedText === InternalSymbolName.Default) {
const file = find(moduleSymbol.declarations, isSourceFile);
if (canHaveSyntheticDefault(file, moduleSymbol, dontResolveAlias)) {
@@ -2395,98 +2416,19 @@ namespace ts {
}
function getTargetOfImportSpecifier(node: ImportSpecifier, dontResolveAlias: boolean): Symbol | undefined {
const resolved = getExternalModuleMember(node.parent.parent.parent, node, dontResolveAlias);
if (resolved && node.parent.parent.isTypeOnly) {
return createTypeOnlyImportOrExport(node.name, resolved);
}
return resolved;
markSymbolOfAliasDeclarationIfTypeOnly(node);
return getExternalModuleMember(node.parent.parent.parent, node, dontResolveAlias);
}
function getTargetOfNamespaceExportDeclaration(node: NamespaceExportDeclaration, dontResolveAlias: boolean): Symbol {
return resolveExternalModuleSymbol(node.parent.symbol, dontResolveAlias);
}
/**
* Creates a type alias symbol with a target symbol for type-only imports and exports.
* The symbol for `A` in `export type { A }` or `export type { A } from "./mod"` has
* `TypeFlags.Alias` so that alias resolution works as usual, but once the target `A`
* has been resolved, we essentially want to pretend we have a type alias to that target.
*/
function createTypeOnlyImportOrExport(sourceNode: ExportSpecifier | Identifier, target: Symbol) {
const symbol = createTypeOnlySymbol(target);
if (!symbol && target !== unknownSymbol) {
const identifier = isExportSpecifier(sourceNode) ? sourceNode.name : sourceNode;
const nameText = idText(identifier);
const diagnostic = error(
identifier,
Diagnostics.Type_only_0_must_reference_a_type_but_1_is_a_value,
isExportSpecifier(sourceNode) ? "export" : "import",
nameText);
const targetDeclaration = target.valueDeclaration ?? target.declarations?.[0];
if (targetDeclaration) {
addRelatedInfo(diagnostic, createDiagnosticForNode(
targetDeclaration,
Diagnostics._0_is_declared_here,
nameText));
}
}
return symbol;
}
function createTypeOnlySymbol(target: Symbol): Symbol | undefined {
if (target.flags & SymbolFlags.ValueModule) {
return createNamespaceModuleForModule(target);
}
if (target.flags & SymbolFlags.Enum) {
return createNamespaceModuleForEnum(target);
}
if (!(target.flags & SymbolFlags.Value)) {
return target;
}
if (target.flags & SymbolFlags.Type) {
const alias = createSymbol(SymbolFlags.TypeAlias, target.escapedName);
alias.declarations = emptyArray;
alias.immediateTarget = target;
return alias;
}
}
function createNamespaceModuleForEnum(enumSymbol: Symbol) {
Debug.assert(!!(enumSymbol.flags & SymbolFlags.Enum));
const symbol = createSymbol(SymbolFlags.NamespaceModule | SymbolFlags.TypeAlias, enumSymbol.escapedName);
symbol.immediateTarget = enumSymbol;
symbol.declarations = enumSymbol.declarations;
if (enumSymbol.exports) {
symbol.exports = createSymbolTable();
enumSymbol.exports.forEach((exportSymbol, key) => {
symbol.exports!.set(key, Debug.assertDefined(createTypeOnlySymbol(exportSymbol)));
});
}
return symbol;
}
function createNamespaceModuleForModule(moduleSymbol: Symbol) {
Debug.assert(!!(moduleSymbol.flags & SymbolFlags.ValueModule));
const filtered = createSymbol(SymbolFlags.NamespaceModule, moduleSymbol.escapedName);
filtered.declarations = moduleSymbol.declarations;
if (moduleSymbol.exports) {
filtered.exports = createSymbolTable();
moduleSymbol.exports.forEach((exportSymbol, key) => {
const typeOnlyExport = createTypeOnlySymbol(exportSymbol);
if (typeOnlyExport) {
filtered.exports!.set(key, typeOnlyExport);
}
});
}
return filtered;
}
function getTargetOfExportSpecifier(node: ExportSpecifier, meaning: SymbolFlags, dontResolveAlias?: boolean) {
const target = node.parent.parent.moduleSpecifier ?
markSymbolOfAliasDeclarationIfTypeOnly(node);
return node.parent.parent.moduleSpecifier ?
getExternalModuleMember(node.parent.parent, node, dontResolveAlias) :
resolveEntityName(node.propertyName || node.name, meaning, /*ignoreErrors*/ false, dontResolveAlias);
return target && node.parent.parent.isTypeOnly ? createTypeOnlyImportOrExport(node, target) : target;
}
function getTargetOfExportAssignment(node: ExportAssignment | BinaryExpression, dontResolveAlias: boolean): Symbol | undefined {
@@ -2588,12 +2530,39 @@ namespace ts {
return links.target;
}
function markSymbolOfAliasDeclarationIfResolvesToTypeOnly(aliasDeclaration: TypeOnlyCompatibleAliasDeclaration | undefined, resolvesToSymbol: Symbol | undefined) {
if (!aliasDeclaration || !resolvesToSymbol) return;
const sourceSymbol = getSymbolOfNode(aliasDeclaration);
const links = getSymbolLinks(sourceSymbol);
if (links.typeOnlyDeclaration === undefined) {
const typeOnly = find(resolvesToSymbol.declarations, isTypeOnlyImportOrExportDeclaration);
links.typeOnlyDeclaration = typeOnly ?? getSymbolLinks(resolvesToSymbol).typeOnlyDeclaration ?? false;
}
}
function markSymbolOfAliasDeclarationIfTypeOnly(aliasDeclaration: TypeOnlyCompatibleAliasDeclaration) {
if (isTypeOnlyImportOrExportDeclaration(aliasDeclaration)) {
const symbol = getSymbolOfNode(aliasDeclaration);
const links = getSymbolLinks(symbol);
links.typeOnlyDeclaration = aliasDeclaration;
}
}
/** Indicates that a symbol directly or indirectly resolves to a type-only import or export. */
function getTypeOnlyAliasDeclaration(symbol: Symbol): TypeOnlyCompatibleAliasDeclaration | undefined {
if (!(symbol.flags & SymbolFlags.Alias)) {
return undefined;
}
const links = getSymbolLinks(symbol);
return links.typeOnlyDeclaration || undefined;
}
function markExportAsReferenced(node: ImportEqualsDeclaration | ExportSpecifier) {
const symbol = getSymbolOfNode(node);
const target = resolveAlias(symbol);
if (target) {
const markAlias = target === unknownSymbol ||
((target.flags & SymbolFlags.Value) && !isConstEnumOrConstEnumOnlyModule(target));
((target.flags & SymbolFlags.Value) && !isConstEnumOrConstEnumOnlyModule(target) && !getTypeOnlyAliasDeclaration(symbol));
if (markAlias) {
markAliasSymbolAsReferenced(symbol);
@@ -2623,6 +2592,15 @@ namespace ts {
}
}
// Aliases that resolve to const enums are not marked as referenced because they are not emitted,
// but their usage in value positions must be tracked to determine if the import can be type-only.
function markConstEnumAliasAsReferenced(symbol: Symbol) {
const links = getSymbolLinks(symbol);
if (!links.constEnumReferenced) {
links.constEnumReferenced = true;
}
}
// This function is only for imports with entity names
function getSymbolOfPartOfRightHandSideOfImportEquals(entityName: EntityName, dontResolveAlias?: boolean): Symbol | undefined {
// There are three things we might try to look for. In the following examples,
@@ -2705,6 +2683,9 @@ namespace ts {
throw Debug.assertNever(name, "Unknown entity name kind.");
}
Debug.assert((getCheckFlags(symbol) & CheckFlags.Instantiated) === 0, "Should never get an instantiated symbol here.");
if (isIdentifier(name) && symbol.flags & SymbolFlags.Alias) {
markSymbolOfAliasDeclarationIfResolvesToTypeOnly(getTypeOnlyCompatibleAliasDeclarationFromName(name), symbol);
}
return (symbol.flags & meaning) || dontResolveAlias ? symbol : resolveAlias(symbol);
}
@@ -3267,7 +3248,7 @@ namespace ts {
}
function symbolIsValue(symbol: Symbol): boolean {
return !!(symbol.flags & SymbolFlags.Value || symbol.flags & SymbolFlags.Alias && resolveAlias(symbol).flags & SymbolFlags.Value);
return !!(symbol.flags & SymbolFlags.Value || symbol.flags & SymbolFlags.Alias && resolveAlias(symbol).flags & SymbolFlags.Value && !getTypeOnlyAliasDeclaration(symbol));
}
function findConstructorDeclaration(node: ClassLikeDeclaration): ConstructorDeclaration | undefined {
@@ -8301,20 +8282,10 @@ namespace ts {
return errorType;
}
let type: Type;
let declaration;
if (isTypeOnlyAlias(symbol)) {
// Symbol is synthetic type alias for type-only import or export.
// See `createSyntheticTypeAlias`.
type = getDeclaredTypeOfSymbol(symbol.immediateTarget);
declaration = symbol.valueDeclaration;
}
else {
declaration = Debug.assertDefined(find(symbol.declarations, isTypeAlias), "Type alias symbol with no valid declaration found");
const typeNode = isJSDocTypeAlias(declaration) ? declaration.typeExpression : declaration.type;
// If typeNode is missing, we will error in checkJSDocTypedefTag.
type = typeNode ? getTypeFromTypeNode(typeNode) : errorType;
}
const declaration = Debug.assertDefined(find(symbol.declarations, isTypeAlias), "Type alias symbol with no valid declaration found");
const typeNode = isJSDocTypeAlias(declaration) ? declaration.typeExpression : declaration.type;
// If typeNode is missing, we will error in checkJSDocTypedefTag.
let type = typeNode ? getTypeFromTypeNode(typeNode) : errorType;
if (popTypeResolution()) {
const typeParameters = getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(symbol);
@@ -11045,9 +11016,6 @@ namespace ts {
if (symbol.flags & (SymbolFlags.Class | SymbolFlags.Interface)) {
return getTypeFromClassOrInterfaceReference(node, symbol);
}
if (isTypeOnlyAlias(symbol)) {
return getTypeReferenceType(node, symbol.immediateTarget);
}
if (symbol.flags & SymbolFlags.TypeAlias) {
return getTypeFromTypeAliasReference(node, symbol);
}
@@ -20306,8 +20274,13 @@ namespace ts {
}
function markAliasReferenced(symbol: Symbol, location: Node) {
if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && ((compilerOptions.preserveConstEnums && isExportOrExportExpression(location)) || !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol)))) {
markAliasSymbolAsReferenced(symbol);
if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !isInTypeQuery(location) && !getTypeOnlyAliasDeclaration(symbol)) {
if (compilerOptions.preserveConstEnums && isExportOrExportExpression(location) || !isConstEnumOrConstEnumOnlyModule(resolveAlias(symbol))) {
markAliasSymbolAsReferenced(symbol);
}
else {
markConstEnumAliasAsReferenced(symbol);
}
}
}
@@ -22866,7 +22839,7 @@ namespace ts {
reactSym.isReferenced = SymbolFlags.All;
// If react symbol is alias, mark it as refereced
if (reactSym.flags & SymbolFlags.Alias) {
if (reactSym.flags & SymbolFlags.Alias && !getTypeOnlyAliasDeclaration(reactSym)) {
markAliasSymbolAsReferenced(reactSym);
}
}
@@ -29673,7 +29646,8 @@ namespace ts {
if (rootSymbol
&& rootSymbol.flags & SymbolFlags.Alias
&& symbolIsValue(rootSymbol)
&& !isConstEnumOrConstEnumOnlyModule(resolveAlias(rootSymbol))) {
&& !isConstEnumOrConstEnumOnlyModule(resolveAlias(rootSymbol))
&& !getTypeOnlyAliasDeclaration(rootSymbol)) {
markAliasSymbolAsReferenced(rootSymbol);
}
}
@@ -33129,17 +33103,15 @@ namespace ts {
}
function importClauseContainsReferencedImport(importClause: ImportClause) {
return importClause.name && isReferenced(importClause)
|| importClause.namedBindings && namedBindingsContainsReferencedImport(importClause.namedBindings);
return forEachImportClauseDeclaration(importClause, declaration => {
return !!getSymbolOfNode(declaration).isReferenced;
});
}
function isReferenced(declaration: Declaration) {
return !!getMergedSymbol(getSymbolOfNode(declaration)).isReferenced;
}
function namedBindingsContainsReferencedImport(namedBindings: NamedImportBindings) {
return isNamespaceImport(namedBindings)
? isReferenced(namedBindings)
: some(namedBindings.elements, isReferenced);
}
function importClauseContainsConstEnumUsedAsValue(importClause: ImportClause) {
return forEachImportClauseDeclaration(importClause, declaration => {
return !!getSymbolLinks(getSymbolOfNode(declaration)).constEnumReferenced;
});
}
function checkImportsForTypeOnlyConversion(sourceFile: SourceFile) {
@@ -33149,15 +33121,12 @@ namespace ts {
statement.importClause &&
!statement.importClause.isTypeOnly &&
importClauseContainsReferencedImport(statement.importClause) &&
!isReferencedAliasDeclaration(statement.importClause, /*checkChildren*/ true)
!isReferencedAliasDeclaration(statement.importClause, /*checkChildren*/ true) &&
!importClauseContainsConstEnumUsedAsValue(statement.importClause)
) {
const isError = compilerOptions.importsNotUsedAsValues === importsNotUsedAsValues.Error;
errorOrSuggestion(
isError,
error(
statement,
isError
? Diagnostics.This_import_is_never_used_as_a_value_and_must_use_import_type_because_the_importsNotUsedAsValues_is_set_to_error
: Diagnostics.This_import_may_be_converted_to_a_type_only_import);
Diagnostics.This_import_is_never_used_as_a_value_and_must_use_import_type_because_the_importsNotUsedAsValues_is_set_to_error);
}
}
}
@@ -33654,7 +33623,10 @@ namespace ts {
});
}
if (!node.isDeclarationFile && isExternalModule(node)) {
if (compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error &&
!node.isDeclarationFile &&
isExternalModule(node)
) {
checkImportsForTypeOnlyConversion(node);
}
@@ -34245,7 +34217,7 @@ namespace ts {
if (isDeclarationNameOrImportPropertyName(node)) {
const symbol = getSymbolAtLocation(node);
if (symbol) {
return isTypeOnlyImportOrExportName(node) ? getDeclaredTypeOfSymbol(symbol) : getTypeOfSymbol(symbol);
return getTypeOfSymbol(symbol);
}
return errorType;
}
@@ -34486,7 +34458,7 @@ namespace ts {
const symbol = getReferencedValueSymbol(node);
// We should only get the declaration of an alias if there isn't a local value
// declaration for the symbol
if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value)) {
if (isNonLocalAlias(symbol, /*excludes*/ SymbolFlags.Value) && !getTypeOnlyAliasDeclaration(symbol)) {
return getDeclarationOfAliasSymbol(symbol);
}
}
@@ -34576,11 +34548,13 @@ namespace ts {
function isValueAliasDeclaration(node: Node): boolean {
switch (node.kind) {
case SyntaxKind.ImportEqualsDeclaration:
return isAliasResolvedToValue(getSymbolOfNode(node) || unknownSymbol);
case SyntaxKind.ImportClause:
case SyntaxKind.NamespaceImport:
case SyntaxKind.ImportSpecifier:
case SyntaxKind.ExportSpecifier:
return isAliasResolvedToValue(getSymbolOfNode(node) || unknownSymbol);
const symbol = getSymbolOfNode(node) || unknownSymbol;
return isAliasResolvedToValue(symbol) && !getTypeOnlyAliasDeclaration(symbol);
case SyntaxKind.ExportDeclaration:
const exportClause = (<ExportDeclaration>node).exportClause;
return !!exportClause && (
@@ -34629,7 +34603,8 @@ namespace ts {
}
const target = getSymbolLinks(symbol!).target; // TODO: GH#18217
if (target && getModifierFlags(node) & ModifierFlags.Export &&
target.flags & SymbolFlags.Value && (compilerOptions.preserveConstEnums || !isConstEnumOrConstEnumOnlyModule(target))) {
target.flags & SymbolFlags.Value &&
(compilerOptions.preserveConstEnums || !isConstEnumOrConstEnumOnlyModule(target))) {
// An `export import ... =` of a value symbol is always considered referenced
return true;
}
+3 -3
View File
@@ -475,9 +475,9 @@ namespace ts {
{
name: "importsNotUsedAsValues",
type: createMapFromTemplate({
remove: importsNotUsedAsValues.Remove,
preserve: importsNotUsedAsValues.Preserve,
error: importsNotUsedAsValues.Error
remove: ImportsNotUsedAsValues.Remove,
preserve: ImportsNotUsedAsValues.Preserve,
error: ImportsNotUsedAsValues.Error
}),
affectsEmit: true,
affectsSemanticDiagnostics: true,
+15 -7
View File
@@ -1055,11 +1055,15 @@
"category": "Error",
"code": 1359
},
"Type-only {0} must reference a type, but '{1}' is a value.": {
"Did you mean to parenthesize this function type?": {
"category": "Error",
"code": 1360
},
"'{0}' cannot be used as a value because it was imported using 'import type'.": {
"category": "Error",
"code": 1361
},
"Enum '{0}' cannot be used as a value because only its type has been imported.": {
"'{0}' cannot be used as a value because it was exported using 'export type'.": {
"category": "Error",
"code": 1362
},
@@ -1099,10 +1103,6 @@
"category": "Error",
"code": 1371
},
"This import may be converted to a type-only import.": {
"category": "Suggestion",
"code": 1372
},
"Convert to type-only import": {
"category": "Message",
"code": 1373
@@ -1115,9 +1115,17 @@
"category": "Error",
"code": 1375
},
"'{0}' was imported here.": {
"category": "Message",
"code": 1376
},
"'{0}' was exported here.": {
"category": "Message",
"code": 1377
},
"Top-level 'await' expressions are only allowed when the 'module' option is set to 'esnext' or 'system', and the 'target' option is set to 'es2017' or higher.": {
"category": "Error",
"code": 1376
"code": 1378
},
"The types of '{0}' are incompatible between these types.": {
"category": "Error",
+3 -3
View File
@@ -2778,8 +2778,8 @@ namespace ts {
// Elide the declaration if the import clause was elided.
const importClause = visitNode(node.importClause, visitImportClause, isImportClause);
return importClause ||
compilerOptions.importsNotUsedAsValues === importsNotUsedAsValues.Preserve ||
compilerOptions.importsNotUsedAsValues === importsNotUsedAsValues.Error
compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve ||
compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Error
? updateImportDeclaration(
node,
/*decorators*/ undefined,
@@ -2931,7 +2931,7 @@ namespace ts {
if (isExternalModuleImportEqualsDeclaration(node)) {
const isReferenced = resolver.isReferencedAliasDeclaration(node);
// If the alias is unreferenced but we want to keep the import, replace with 'import "mod"'.
if (!isReferenced && compilerOptions.importsNotUsedAsValues === importsNotUsedAsValues.Preserve) {
if (!isReferenced && compilerOptions.importsNotUsedAsValues === ImportsNotUsedAsValues.Preserve) {
return setOriginalNode(
setTextRange(
createImportDeclaration(
+6 -3
View File
@@ -2538,6 +2538,7 @@ namespace ts {
}
export type ImportOrExportSpecifier = ImportSpecifier | ExportSpecifier;
export type TypeOnlyCompatibleAliasDeclaration = ImportClause | NamespaceImport | ImportOrExportSpecifier;
/**
* This is either an `export =` or an `export default` declaration.
@@ -4062,7 +4063,8 @@ namespace ts {
instantiations?: Map<Type>; // Instantiations of generic type alias (undefined if non-generic)
inferredClassSymbol?: Map<TransientSymbol>; // Symbol of an inferred ES5 constructor function
mapper?: TypeMapper; // Type mapper for instantiation alias
referenced?: boolean; // True if alias symbol has been referenced as a value
referenced?: boolean; // True if alias symbol has been referenced as a value that can be emitted
constEnumReferenced?: boolean; // True if alias symbol resolves to a const enum and is referenced as a value ('referenced' will be false)
containingType?: UnionOrIntersectionType; // Containing union or intersection type for synthetic property
leftSpread?: Symbol; // Left source for synthetic spread property
rightSpread?: Symbol; // Right source for synthetic spread property
@@ -4085,6 +4087,7 @@ namespace ts {
deferralConstituents?: Type[]; // Calculated list of constituents for a deferred type
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?: TypeOnlyCompatibleAliasDeclaration | false; // First resolved alias declaration that makes the symbol only usable in type constructs
}
/* @internal */
@@ -5046,7 +5049,7 @@ namespace ts {
/*@internal*/generateCpuProfile?: string;
/*@internal*/help?: boolean;
importHelpers?: boolean;
importsNotUsedAsValues?: importsNotUsedAsValues;
importsNotUsedAsValues?: ImportsNotUsedAsValues;
/*@internal*/init?: boolean;
inlineSourceMap?: boolean;
inlineSources?: boolean;
@@ -5168,7 +5171,7 @@ namespace ts {
ReactNative = 3
}
export const enum importsNotUsedAsValues {
export const enum ImportsNotUsedAsValues {
Remove,
Preserve,
Error
+44 -8
View File
@@ -44,14 +44,6 @@ namespace ts {
return (symbol.flags & SymbolFlags.Transient) !== 0;
}
export function isTypeOnlyAlias(symbol: Symbol): symbol is TransientSymbol & { immediateTarget: Symbol } {
return isTransientSymbol(symbol) && !!symbol.immediateTarget;
}
export function isTypeOnlyEnumAlias(symbol: Symbol): ReturnType<typeof isTypeOnlyAlias> {
return isTypeOnlyAlias(symbol) && !!(symbol.immediateTarget.flags & SymbolFlags.Enum);
}
const stringWriter = createSingleLineStringWriter();
function createSingleLineStringWriter(): EmitTextWriter {
@@ -1779,6 +1771,27 @@ namespace ts {
}
}
export function isPartOfTypeQuery(node: Node) {
while (node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.Identifier) {
node = node.parent;
}
return node.kind === SyntaxKind.TypeQuery;
}
export function isPartOfPossiblyValidTypeOrAbstractComputedPropertyName(node: Node) {
while (node.kind === SyntaxKind.Identifier || node.kind === SyntaxKind.PropertyAccessExpression) {
node = node.parent;
}
if (node.kind !== SyntaxKind.ComputedPropertyName) {
return false;
}
if (hasModifier(node.parent, ModifierFlags.Abstract)) {
return true;
}
const containerKind = node.parent.parent.kind;
return containerKind === SyntaxKind.InterfaceDeclaration || containerKind === SyntaxKind.TypeLiteral;
}
export function isExternalModuleImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration & { moduleReference: ExternalModuleReference } {
return node.kind === SyntaxKind.ImportEqualsDeclaration && (<ImportEqualsDeclaration>node).moduleReference.kind === SyntaxKind.ExternalModuleReference;
}
@@ -2285,6 +2298,19 @@ namespace ts {
return node.kind === SyntaxKind.ImportDeclaration && !!node.importClause && !!node.importClause.name;
}
export function forEachImportClauseDeclaration<T>(node: ImportClause, action: (declaration: ImportClause | NamespaceImport | ImportSpecifier) => T | undefined): T | undefined {
if (node.name) {
const result = action(node);
if (result) return result;
}
if (node.namedBindings) {
const result = isNamespaceImport(node.namedBindings)
? action(node.namedBindings)
: forEach(node.namedBindings.elements, action);
if (result) return result;
}
}
export function hasQuestionToken(node: Node) {
if (node) {
switch (node.kind) {
@@ -2734,6 +2760,16 @@ namespace ts {
node.kind === SyntaxKind.PropertyAssignment && isAliasableExpression((node as PropertyAssignment).initializer);
}
export function getTypeOnlyCompatibleAliasDeclarationFromName(node: Identifier): TypeOnlyCompatibleAliasDeclaration | undefined {
switch (node.parent.kind) {
case SyntaxKind.ImportClause:
case SyntaxKind.ImportSpecifier:
case SyntaxKind.NamespaceImport:
case SyntaxKind.ExportSpecifier:
return node.parent as TypeOnlyCompatibleAliasDeclaration;
}
}
function isAliasableExpression(e: Expression) {
return isEntityNameExpression(e) || isClassExpression(e);
}
+6 -7
View File
@@ -1721,16 +1721,15 @@ namespace ts {
return isImportSpecifier(node) || isExportSpecifier(node);
}
export function isTypeOnlyImportOrExportName(node: Node): boolean {
if (node.kind !== SyntaxKind.Identifier) {
return false;
}
switch (node.parent.kind) {
export function isTypeOnlyImportOrExportDeclaration(node: Node): node is TypeOnlyCompatibleAliasDeclaration {
switch (node.kind) {
case SyntaxKind.ImportSpecifier:
case SyntaxKind.ExportSpecifier:
return (node.parent as ImportSpecifier | ExportSpecifier).parent.parent.isTypeOnly;
return (node as ImportOrExportSpecifier).parent.parent.isTypeOnly;
case SyntaxKind.NamespaceImport:
return (node as NamespaceImport).parent.isTypeOnly;
case SyntaxKind.ImportClause:
return (node.parent as ImportClause).isTypeOnly;
return (node as ImportClause).isTypeOnly;
default:
return false;
}
-9
View File
@@ -2,10 +2,6 @@
namespace ts.SymbolDisplay {
// TODO(drosen): use contextual SemanticMeaning.
export function getSymbolKind(typeChecker: TypeChecker, symbol: Symbol, location: Node): ScriptElementKind {
while (isTypeOnlyAlias(symbol)) {
symbol = symbol.immediateTarget;
}
const result = getSymbolKindOfConstructorPropertyMethodAccessorFunctionOrVar(typeChecker, symbol, location);
if (result !== ScriptElementKind.unknown) {
return result;
@@ -125,11 +121,6 @@ namespace ts.SymbolDisplay {
// TODO(drosen): Currently completion entry details passes the SemanticMeaning.All instead of using semanticMeaning of location
export function getSymbolDisplayPartsDocumentationAndSymbolKind(typeChecker: TypeChecker, symbol: Symbol, sourceFile: SourceFile, enclosingDeclaration: Node | undefined,
location: Node, semanticMeaning = getMeaningFromLocation(location), alias?: Symbol): SymbolDisplayPartsDocumentationAndSymbolKind {
while (isTypeOnlyAlias(symbol)) {
symbol = symbol.immediateTarget;
}
const displayParts: SymbolDisplayPart[] = [];
let documentation: SymbolDisplayPart[] = [];
let tags: JSDocTagInfo[] = [];