diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 630a4640a51e4..d0c227b1541c4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -6085,6 +6085,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { return result; } } + context.tracker.reportInferenceFallback(existing); return undefined; } @@ -8301,6 +8302,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { // If the symbol is found both in declaration scope and in current scope then it shoudl point to the same reference (symAtLocation && sym && !getSymbolIfSameReference(getExportSymbolOfValueSymbolIfExported(symAtLocation), sym)) ) { + // In isolated declaration we will not do rest parameter expansion so there is no need to report on these. + if (symAtLocation !== unknownSymbol) { + context.tracker.reportInferenceFallback(node); + } introducesError = true; return { introducesError, node, sym }; } @@ -8321,6 +8326,7 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker { !isDeclarationName(node) && isSymbolAccessible(sym, context.enclosingDeclaration, meaning, /*shouldComputeAliasesToMakeVisible*/ false).accessibility !== SymbolAccessibility.Accessible ) { + context.tracker.reportInferenceFallback(node); introducesError = true; } else { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 72c7c4e90ded1..71b2c712b91cf 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -7018,6 +7018,10 @@ "category": "Error", "code": 9038 }, + "Type containing private name '{0}' can't be used with --isolatedDeclarations.": { + "category": "Error", + "code": 9039 + }, "JSX attributes must only be assigned a non-empty 'expression'.": { "category": "Error", "code": 17000 diff --git a/src/compiler/transformers/declarations/diagnostics.ts b/src/compiler/transformers/declarations/diagnostics.ts index 958d72c0ccc1f..22bb998ae60bf 100644 --- a/src/compiler/transformers/declarations/diagnostics.ts +++ b/src/compiler/transformers/declarations/diagnostics.ts @@ -19,12 +19,14 @@ import { DiagnosticWithLocation, ElementAccessExpression, EmitResolver, + EntityNameOrEntityNameExpression, ExportAssignment, Expression, ExpressionWithTypeArguments, findAncestor, FunctionDeclaration, FunctionExpression, + FunctionLikeDeclaration, GetAccessorDeclaration, getAllAccessorDeclarations, getNameOfDeclaration, @@ -40,9 +42,12 @@ import { isConstructorDeclaration, isConstructSignatureDeclaration, isElementAccessExpression, + isEntityName, + isEntityNameExpression, isExportAssignment, isExpressionWithTypeArguments, isFunctionDeclaration, + isFunctionLikeDeclaration, isGetAccessor, isHeritageClause, isImportEqualsDeclaration, @@ -53,15 +58,18 @@ import { isParameter, isParameterPropertyDeclaration, isParenthesizedExpression, + isPartOfTypeNode, isPropertyAccessExpression, isPropertyDeclaration, isPropertySignature, + isReturnStatement, isSetAccessor, isStatement, isStatic, isTypeAliasDeclaration, isTypeAssertionExpression, isTypeParameterDeclaration, + isTypeQueryNode, isVariableDeclaration, JSDocCallbackTag, JSDocEnumTag, @@ -658,6 +666,9 @@ export function createGetIsolatedDeclarationErrors(resolver: EmitResolver) { if (heritageClause) { return createDiagnosticForNode(node, Diagnostics.Extends_clause_can_t_contain_an_expression_with_isolatedDeclarations); } + if ((isPartOfTypeNode(node) || isTypeQueryNode(node.parent)) && (isEntityName(node) || isEntityNameExpression(node))) { + return createEntityInTypeNodeError(node); + } Debug.type(node); switch (node.kind) { case SyntaxKind.GetAccessor: @@ -694,8 +705,15 @@ export function createGetIsolatedDeclarationErrors(resolver: EmitResolver) { } function findNearestDeclaration(node: Node) { - const result = findAncestor(node, n => isExportAssignment(n) || (isStatement(n) ? "quit" : isVariableDeclaration(n) || isPropertyDeclaration(n) || isParameter(n))); - return result as VariableDeclaration | PropertyDeclaration | ParameterDeclaration | ExportAssignment | undefined; + const result = findAncestor(node, n => isExportAssignment(n) || isStatement(n) || isVariableDeclaration(n) || isPropertyDeclaration(n) || isParameter(n)); + if (!result) return undefined; + + if (isExportAssignment(result)) return result; + + if (isReturnStatement(result)) { + return findAncestor(result, (n): n is Exclude => isFunctionLikeDeclaration(n) && !isConstructorDeclaration(n)); + } + return (isStatement(result) ? undefined : result) as VariableDeclaration | PropertyDeclaration | ParameterDeclaration | ExportAssignment | undefined; } function createAccessorTypeError(node: GetAccessorDeclaration | SetAccessorDeclaration) { @@ -712,31 +730,27 @@ export function createGetIsolatedDeclarationErrors(resolver: EmitResolver) { } return diag; } - function createObjectLiteralError(node: ShorthandPropertyAssignment | SpreadAssignment | ComputedPropertyName) { - const diag = createDiagnosticForNode(node, errorByDeclarationKind[node.kind]); + function addParentDeclarationRelatedInfo(node: Node, diag: DiagnosticWithLocation) { const parentDeclaration = findNearestDeclaration(node); if (parentDeclaration) { - const targetStr = isExportAssignment(parentDeclaration) ? "" : getTextOfNode(parentDeclaration.name, /*includeTrivia*/ false); + const targetStr = isExportAssignment(parentDeclaration) || !parentDeclaration.name ? "" : getTextOfNode(parentDeclaration.name, /*includeTrivia*/ false); addRelatedInfo(diag, createDiagnosticForNode(parentDeclaration, relatedSuggestionByDeclarationKind[parentDeclaration.kind], targetStr)); } return diag; } + function createObjectLiteralError(node: ShorthandPropertyAssignment | SpreadAssignment | ComputedPropertyName) { + const diag = createDiagnosticForNode(node, errorByDeclarationKind[node.kind]); + addParentDeclarationRelatedInfo(node, diag); + return diag; + } function createArrayLiteralError(node: ArrayLiteralExpression | SpreadElement) { const diag = createDiagnosticForNode(node, errorByDeclarationKind[node.kind]); - const parentDeclaration = findNearestDeclaration(node); - if (parentDeclaration) { - const targetStr = isExportAssignment(parentDeclaration) ? "" : getTextOfNode(parentDeclaration.name, /*includeTrivia*/ false); - addRelatedInfo(diag, createDiagnosticForNode(parentDeclaration, relatedSuggestionByDeclarationKind[parentDeclaration.kind], targetStr)); - } + addParentDeclarationRelatedInfo(node, diag); return diag; } function createReturnTypeError(node: FunctionDeclaration | FunctionExpression | ArrowFunction | MethodDeclaration | ConstructSignatureDeclaration) { const diag = createDiagnosticForNode(node, errorByDeclarationKind[node.kind]); - const parentDeclaration = findNearestDeclaration(node); - if (parentDeclaration) { - const targetStr = isExportAssignment(parentDeclaration) ? "" : getTextOfNode(parentDeclaration.name, /*includeTrivia*/ false); - addRelatedInfo(diag, createDiagnosticForNode(parentDeclaration, relatedSuggestionByDeclarationKind[parentDeclaration.kind], targetStr)); - } + addParentDeclarationRelatedInfo(node, diag); addRelatedInfo(diag, createDiagnosticForNode(node, relatedSuggestionByDeclarationKind[node.kind])); return diag; } @@ -768,12 +782,18 @@ export function createGetIsolatedDeclarationErrors(resolver: EmitResolver) { function createClassExpressionError(node: Expression) { return createExpressionError(node, Diagnostics.Inference_from_class_expressions_is_not_supported_with_isolatedDeclarations); } + function createEntityInTypeNodeError(node: EntityNameOrEntityNameExpression) { + const diag = createDiagnosticForNode(node, Diagnostics.Type_containing_private_name_0_can_t_be_used_with_isolatedDeclarations, getTextOfNode(node, /*includeTrivia*/ false)); + addParentDeclarationRelatedInfo(node, diag); + return diag; + } function createExpressionError(node: Expression, diagnosticMessage?: DiagnosticMessage) { const parentDeclaration = findNearestDeclaration(node); let diag: DiagnosticWithLocation; if (parentDeclaration) { - const targetStr = isExportAssignment(parentDeclaration) ? "" : getTextOfNode(parentDeclaration.name, /*includeTrivia*/ false); + const targetStr = isExportAssignment(parentDeclaration) || !parentDeclaration.name ? "" : getTextOfNode(parentDeclaration.name, /*includeTrivia*/ false); const parent = findAncestor(node.parent, n => isExportAssignment(n) || (isStatement(n) ? "quit" : !isParenthesizedExpression(n) && !isTypeAssertionExpression(n) && !isAsExpression(n))); + if (parentDeclaration === parent) { diag = createDiagnosticForNode(node, diagnosticMessage ?? errorByDeclarationKind[parentDeclaration.kind]); addRelatedInfo(diag, createDiagnosticForNode(parentDeclaration, relatedSuggestionByDeclarationKind[parentDeclaration.kind], targetStr)); diff --git a/src/services/codefixes/fixMissingTypeAnnotationOnExports.ts b/src/services/codefixes/fixMissingTypeAnnotationOnExports.ts index 86e68aa2b49d0..1b2f93d06c78d 100644 --- a/src/services/codefixes/fixMissingTypeAnnotationOnExports.ts +++ b/src/services/codefixes/fixMissingTypeAnnotationOnExports.ts @@ -65,6 +65,7 @@ import { isSpreadAssignment, isSpreadElement, isStatement, + isTypeNode, isValueSignatureDeclaration, isVariableDeclaration, ModifierFlags, @@ -132,6 +133,7 @@ const errorCodes = [ Diagnostics.Only_const_arrays_can_be_inferred_with_isolatedDeclarations.code, Diagnostics.Assigning_properties_to_functions_without_declaring_them_is_not_supported_with_isolatedDeclarations_Add_an_explicit_declaration_for_the_properties_assigned_to_this_function.code, Diagnostics.Declaration_emit_for_this_parameter_requires_implicitly_adding_undefined_to_it_s_type_This_is_not_supported_with_isolatedDeclarations.code, + Diagnostics.Type_containing_private_name_0_can_t_be_used_with_isolatedDeclarations.code, Diagnostics.Add_satisfies_and_a_type_assertion_to_this_expression_satisfies_T_as_T_to_make_the_type_explicit.code, ]; @@ -352,7 +354,7 @@ function withContext( return undefined; } // No support for typeof in extends clauses - if (isExpressionTarget && findAncestor(targetNode, isHeritageClause)) { + if (isExpressionTarget && (findAncestor(targetNode, isHeritageClause) || findAncestor(targetNode, isTypeNode))) { return undefined; } // Can't inline type spread elements. Whatever you do isolated declarations will not infer from them diff --git a/tests/baselines/reference/transpile/declarationNotInScopeTypes.d.ts b/tests/baselines/reference/transpile/declarationNotInScopeTypes.d.ts new file mode 100644 index 0000000000000..99742621419ca --- /dev/null +++ b/tests/baselines/reference/transpile/declarationNotInScopeTypes.d.ts @@ -0,0 +1,49 @@ +//// [variables.ts] //// +const x = ""; +export function one() { + return {} as typeof x; +} + +export function two() { + const y = ""; + return {} as typeof y; +} + +export function three() { + type Z = string; + return {} as Z; +} +//// [variables.d.ts] //// +declare const x = ""; +export declare function one(): typeof x; +export declare function two(): ""; +export declare function three(): string; +export {}; + + +//// [Diagnostics reported] +variables.ts(8,25): error TS9039: Type containing private name 'y' can't be used with --isolatedDeclarations. +variables.ts(13,18): error TS9039: Type containing private name 'Z' can't be used with --isolatedDeclarations. + + +==== variables.ts (2 errors) ==== + const x = ""; + export function one() { + return {} as typeof x; + } + + export function two() { + const y = ""; + return {} as typeof y; + ~ +!!! error TS9039: Type containing private name 'y' can't be used with --isolatedDeclarations. +!!! related TS9031 variables.ts:6:17: Add a return type to the function declaration. + } + + export function three() { + type Z = string; + return {} as Z; + ~ +!!! error TS9039: Type containing private name 'Z' can't be used with --isolatedDeclarations. +!!! related TS9031 variables.ts:11:17: Add a return type to the function declaration. + } diff --git a/tests/baselines/reference/transpile/declarationNotInScopeTypes.js b/tests/baselines/reference/transpile/declarationNotInScopeTypes.js new file mode 100644 index 0000000000000..0cdd7d39fd145 --- /dev/null +++ b/tests/baselines/reference/transpile/declarationNotInScopeTypes.js @@ -0,0 +1,27 @@ +//// [variables.ts] //// +const x = ""; +export function one() { + return {} as typeof x; +} + +export function two() { + const y = ""; + return {} as typeof y; +} + +export function three() { + type Z = string; + return {} as Z; +} +//// [variables.js] //// +const x = ""; +export function one() { + return {}; +} +export function two() { + const y = ""; + return {}; +} +export function three() { + return {}; +} diff --git a/tests/cases/fourslash/codeFixMissingTypeAnnotationOnExports49-private-name.ts b/tests/cases/fourslash/codeFixMissingTypeAnnotationOnExports49-private-name.ts new file mode 100644 index 0000000000000..72403ecf4a713 --- /dev/null +++ b/tests/cases/fourslash/codeFixMissingTypeAnnotationOnExports49-private-name.ts @@ -0,0 +1,48 @@ +/// + +// @isolatedDeclarations: true +// @declaration: true +// @moduleResolution: node +// @target: es2018 +// @jsx: react-jsx + +////export function two() { +//// const y = ""; +//// return {} as typeof y; +////} +//// +////export function three() { +//// type Z = string; +//// return {} as Z; +////} + +verify.codeFix({ + description: "Add return type '\"\"'", + index: 0, + newFileContent: +`export function two(): "" { + const y = ""; + return {} as typeof y; +} + +export function three() { + type Z = string; + return {} as Z; +}`, +}); + + +verify.codeFix({ + description: "Add return type 'string'", + index: 1, + newFileContent: +`export function two() { + const y = ""; + return {} as typeof y; +} + +export function three(): string { + type Z = string; + return {} as Z; +}`, +}); \ No newline at end of file diff --git a/tests/cases/transpile/declarationNotInScopeTypes.ts b/tests/cases/transpile/declarationNotInScopeTypes.ts new file mode 100644 index 0000000000000..9d407bd9dd716 --- /dev/null +++ b/tests/cases/transpile/declarationNotInScopeTypes.ts @@ -0,0 +1,17 @@ +// @declaration: true +// @target: es6 +// @filename: variables.ts +const x = ""; +export function one() { + return {} as typeof x; +} + +export function two() { + const y = ""; + return {} as typeof y; +} + +export function three() { + type Z = string; + return {} as Z; +} \ No newline at end of file