Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 27 commits into from
Jan 23, 2020
Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
b40284c
Change type-only semantics to allow type queries
andrewbranch Jan 8, 2020
2ae9edc
Don’t error using type-only import in ambient context
andrewbranch Jan 8, 2020
db93eb2
Fix default import
andrewbranch Jan 8, 2020
a2548c8
Fix namespace import
andrewbranch Jan 8, 2020
8d3f167
Update more baselines
andrewbranch Jan 8, 2020
b56ad7d
Prevent circular resolution
andrewbranch Jan 8, 2020
0547a3d
Track const enum expression usage
andrewbranch Jan 9, 2020
124dcd6
Update baselines
andrewbranch Jan 9, 2020
2dd3690
Perf tuning 1
andrewbranch Jan 10, 2020
eefa335
Test commit for perf impact
andrewbranch Jan 10, 2020
9a24cba
Weave type-only alias declaration finding into alias resolution
andrewbranch Jan 13, 2020
875349d
Fix namespace import of type-only exported symbols
andrewbranch Jan 14, 2020
171e314
type-only exports do not contribute to the module object type
andrewbranch Jan 14, 2020
910dd84
Update APIs
andrewbranch Jan 14, 2020
2a7c472
Fix enum casing, remove type-only conversion suggestion
andrewbranch Jan 14, 2020
11ba11a
Short circuit type-only checks in resolveEntityName faster
andrewbranch Jan 14, 2020
0c28994
Fix casing in API
andrewbranch Jan 14, 2020
40a2c3c
Remove unused parameter
andrewbranch Jan 14, 2020
1cbfb81
Merge branch 'master' into type-only-2
andrewbranch Jan 14, 2020
124d746
Fix error on qualified names in type queries
andrewbranch Jan 14, 2020
bbbcbd5
Merge branch 'master' into type-only-2
andrewbranch Jan 15, 2020
1044c5c
Allow type-only imports in computed property names
andrewbranch Jan 15, 2020
a5ca492
Fix computed property names of types and abstract members
andrewbranch Jan 16, 2020
8571a8a
Remove unused util
andrewbranch Jan 16, 2020
8b4c235
Commit missing baselines
andrewbranch Jan 16, 2020
be5f50f
Merge branch master into type-only-2
andrewbranch Jan 21, 2020
19b3206
Rename “check” functions so as not to overload the word “check”
andrewbranch Jan 23, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
287 changes: 131 additions & 156 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
category: Diagnostics.Advanced_Options,
Expand Down
19 changes: 12 additions & 7 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -1059,11 +1059,11 @@
"category": "Error",
"code": 1360
},
"Type-only {0} must reference a type, but '{1}' is a value.": {
"'{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
},
Expand All @@ -1085,7 +1085,7 @@
},
"Split all invalid type-only imports": {
"category": "Message",
"code": 1377
"code": 1367
},
"Specify emit/checking behavior for imports that are only used for types": {
"category": "Message",
Expand All @@ -1103,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
Expand All @@ -1119,6 +1115,15 @@
"category": "Error",
"code": 1375
},
"'{0}' was imported here.": {
"category": "Message",
"code": 1376
},
"'{0}' was exported here.": {
"category": "Message",
"code": 1377
},

"The types of '{0}' are incompatible between these types.": {
"category": "Error",
"code": 2200
Expand Down
6 changes: 3 additions & 3 deletions src/compiler/transformers/ts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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(
Expand Down
9 changes: 6 additions & 3 deletions src/compiler/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -4059,7 +4060,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
Expand All @@ -4082,6 +4084,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 */
Expand Down Expand Up @@ -5043,7 +5046,7 @@ namespace ts {
/*@internal*/generateCpuProfile?: string;
/*@internal*/help?: boolean;
importHelpers?: boolean;
importsNotUsedAsValues?: importsNotUsedAsValues;
importsNotUsedAsValues?: ImportsNotUsedAsValues;
/*@internal*/init?: boolean;
inlineSourceMap?: boolean;
inlineSources?: boolean;
Expand Down Expand Up @@ -5165,7 +5168,7 @@ namespace ts {
ReactNative = 3
}

export const enum importsNotUsedAsValues {
export const enum ImportsNotUsedAsValues {
Remove,
Preserve,
Error
Expand Down
56 changes: 48 additions & 8 deletions src/compiler/utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -1779,6 +1771,31 @@ namespace ts {
}
}

export function isPartOfTypeQuery(node: Node) {
while (node.kind === SyntaxKind.QualifiedName || node.kind === SyntaxKind.Identifier) {
andrewbranch marked this conversation as resolved.
Show resolved Hide resolved
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 isAbstractDeclarationName(node: Node) {
andrewbranch marked this conversation as resolved.
Show resolved Hide resolved
return isDeclarationName(node) && hasModifier(node, ModifierFlags.Abstract);
}

export function isExternalModuleImportEqualsDeclaration(node: Node): node is ImportEqualsDeclaration & { moduleReference: ExternalModuleReference } {
return node.kind === SyntaxKind.ImportEqualsDeclaration && (<ImportEqualsDeclaration>node).moduleReference.kind === SyntaxKind.ExternalModuleReference;
}
Expand Down Expand Up @@ -2285,6 +2302,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) {
Expand Down Expand Up @@ -2734,6 +2764,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);
}
Expand Down
13 changes: 6 additions & 7 deletions src/compiler/utilitiesPublic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
9 changes: 0 additions & 9 deletions src/services/symbolDisplay.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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[] = [];
Expand Down
25 changes: 25 additions & 0 deletions tests/baselines/reference/ambient.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//// [tests/cases/conformance/externalModules/typeOnly/ambient.ts] ////

//// [a.ts]
export class A { a!: string }

//// [b.ts]
import type { A } from './a';
declare class B extends A {}
declare namespace ns {
class C extends A {}
}


//// [a.js]
"use strict";
exports.__esModule = true;
var A = /** @class */ (function () {
function A() {
}
return A;
}());
exports.A = A;
//// [b.js]
"use strict";
exports.__esModule = true;
21 changes: 21 additions & 0 deletions tests/baselines/reference/ambient.symbols
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
=== /a.ts ===
export class A { a!: string }
>A : Symbol(A, Decl(a.ts, 0, 0))
>a : Symbol(A.a, Decl(a.ts, 0, 16))

=== /b.ts ===
import type { A } from './a';
>A : Symbol(A, Decl(b.ts, 0, 13))

declare class B extends A {}
>B : Symbol(B, Decl(b.ts, 0, 29))
>A : Symbol(A, Decl(b.ts, 0, 13))

declare namespace ns {
>ns : Symbol(ns, Decl(b.ts, 1, 28))

class C extends A {}
>C : Symbol(C, Decl(b.ts, 2, 22))
>A : Symbol(A, Decl(b.ts, 0, 13))
}

21 changes: 21 additions & 0 deletions tests/baselines/reference/ambient.types
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
=== /a.ts ===
export class A { a!: string }
>A : A
>a : string

=== /b.ts ===
import type { A } from './a';
>A : A

declare class B extends A {}
>B : B
>A : A

declare namespace ns {
>ns : typeof ns

class C extends A {}
>C : C
>A : A
}

7 changes: 4 additions & 3 deletions tests/baselines/reference/api/tsserverlibrary.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1547,6 +1547,7 @@ declare namespace ts {
name: Identifier;
}
export type ImportOrExportSpecifier = ImportSpecifier | ExportSpecifier;
export type TypeOnlyCompatibleAliasDeclaration = ImportClause | NamespaceImport | ImportOrExportSpecifier;
/**
* This is either an `export =` or an `export default` declaration.
* Unless `isExportEquals` is set, this node was parsed as an `export default`.
Expand Down Expand Up @@ -2651,7 +2652,7 @@ declare namespace ts {
experimentalDecorators?: boolean;
forceConsistentCasingInFileNames?: boolean;
importHelpers?: boolean;
importsNotUsedAsValues?: importsNotUsedAsValues;
importsNotUsedAsValues?: ImportsNotUsedAsValues;
inlineSourceMap?: boolean;
inlineSources?: boolean;
isolatedModules?: boolean;
Expand Down Expand Up @@ -2750,7 +2751,7 @@ declare namespace ts {
React = 2,
ReactNative = 3
}
export enum importsNotUsedAsValues {
export enum ImportsNotUsedAsValues {
Remove = 0,
Preserve = 1,
Error = 2
Expand Down Expand Up @@ -3739,7 +3740,7 @@ declare namespace ts {
function isTemplateLiteralToken(node: Node): node is TemplateLiteralToken;
function isTemplateMiddleOrTemplateTail(node: Node): node is TemplateMiddle | TemplateTail;
function isImportOrExportSpecifier(node: Node): node is ImportSpecifier | ExportSpecifier;
function isTypeOnlyImportOrExportName(node: Node): boolean;
function isTypeOnlyImportOrExportDeclaration(node: Node): node is TypeOnlyCompatibleAliasDeclaration;
function isStringTextContainingNode(node: Node): node is StringLiteral | TemplateLiteralToken;
function isModifier(node: Node): node is Modifier;
function isEntityName(node: Node): node is EntityName;
Expand Down
7 changes: 4 additions & 3 deletions tests/baselines/reference/api/typescript.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1547,6 +1547,7 @@ declare namespace ts {
name: Identifier;
}
export type ImportOrExportSpecifier = ImportSpecifier | ExportSpecifier;
export type TypeOnlyCompatibleAliasDeclaration = ImportClause | NamespaceImport | ImportOrExportSpecifier;
/**
* This is either an `export =` or an `export default` declaration.
* Unless `isExportEquals` is set, this node was parsed as an `export default`.
Expand Down Expand Up @@ -2651,7 +2652,7 @@ declare namespace ts {
experimentalDecorators?: boolean;
forceConsistentCasingInFileNames?: boolean;
importHelpers?: boolean;
importsNotUsedAsValues?: importsNotUsedAsValues;
importsNotUsedAsValues?: ImportsNotUsedAsValues;
inlineSourceMap?: boolean;
inlineSources?: boolean;
isolatedModules?: boolean;
Expand Down Expand Up @@ -2750,7 +2751,7 @@ declare namespace ts {
React = 2,
ReactNative = 3
}
export enum importsNotUsedAsValues {
export enum ImportsNotUsedAsValues {
Remove = 0,
Preserve = 1,
Error = 2
Expand Down Expand Up @@ -3739,7 +3740,7 @@ declare namespace ts {
function isTemplateLiteralToken(node: Node): node is TemplateLiteralToken;
function isTemplateMiddleOrTemplateTail(node: Node): node is TemplateMiddle | TemplateTail;
function isImportOrExportSpecifier(node: Node): node is ImportSpecifier | ExportSpecifier;
function isTypeOnlyImportOrExportName(node: Node): boolean;
function isTypeOnlyImportOrExportDeclaration(node: Node): node is TypeOnlyCompatibleAliasDeclaration;
function isStringTextContainingNode(node: Node): node is StringLiteral | TemplateLiteralToken;
function isModifier(node: Node): node is Modifier;
function isEntityName(node: Node): node is EntityName;
Expand Down
Loading