Skip to content

Commit 00aeef9

Browse files
committed
Provide a fixer to add type for standalone expando functions
Update reference tests after the fixer fixes it and becomes identical or update the diff reason Signed-off-by: Hana Joo <hanajoo@google.com>
1 parent 35da7eb commit 00aeef9

46 files changed

Lines changed: 487 additions & 1832 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/compiler/diagnosticMessages.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7220,6 +7220,10 @@
72207220
"category": "Message",
72217221
"code": 90070
72227222
},
7223+
"Annotate types of properties expando function in a namespace": {
7224+
"category": "Message",
7225+
"code": 90071
7226+
},
72237227

72247228
"Convert function to an ES2015 class": {
72257229
"category": "Message",

src/services/codefixes/fixMissingTypeAnnotationOnExports.ts

Lines changed: 57 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import {
2626
factory,
2727
FileTextChanges,
2828
findAncestor,
29+
FunctionDeclaration,
2930
GeneratedIdentifierFlags,
3031
getEmitScriptTarget,
3132
getSourceFileOfNode,
@@ -48,6 +49,7 @@ import {
4849
isEnumMember,
4950
isExpandoPropertyDeclaration,
5051
isExpression,
52+
isFunctionDeclaration,
5153
isFunctionExpressionOrArrowFunction,
5254
isHeritageClause,
5355
isIdentifier,
@@ -69,6 +71,7 @@ import {
6971
isValueSignatureDeclaration,
7072
isVariableDeclaration,
7173
ModifierFlags,
74+
ModifierLike,
7275
Node,
7376
NodeArray,
7477
NodeBuilderFlags,
@@ -224,6 +227,7 @@ function withChanges<T>(
224227
const scriptTarget = getEmitScriptTarget(program.getCompilerOptions());
225228
const importAdder = createImportAdder(context.sourceFile, context.program, context.preferences, context.host);
226229
const fixedNodes = new Set<Node>();
230+
const expandoPropertiesAdded = new Set<Node>();
227231

228232
const result = cb({ addFullAnnotation, addInlineAnnotation, extractAsVariable });
229233
importAdder.writeFixes(changeTracker);
@@ -234,16 +238,61 @@ function withChanges<T>(
234238

235239
function addFullAnnotation(span: TextSpan) {
236240
const nodeWithDiag = getTokenAtPosition(sourceFile, span.start);
237-
const nodeWithNoType = findNearestParentWithTypeAnnotation(nodeWithDiag) ?? findExpandoFunction(nodeWithDiag);
241+
const expandoFunction = findExpandoFunction(nodeWithDiag);
242+
if (expandoFunction) {
243+
if (isFunctionDeclaration(expandoFunction)) {
244+
return createNamespaceForExpandoProperties(expandoFunction);
245+
}
246+
return fixupForIsolatedDeclarations(expandoFunction);
247+
}
248+
const nodeWithNoType = findNearestParentWithTypeAnnotation(nodeWithDiag)
238249
if (nodeWithNoType) {
239250
return fixupForIsolatedDeclarations(nodeWithNoType);
240251
}
241252
return undefined;
242253
}
243254

255+
function createNamespaceForExpandoProperties(expandoFunc: FunctionDeclaration): DiagnosticOrDiagnosticAndArguments|undefined {
256+
if (expandoPropertiesAdded?.has(expandoFunc)) return undefined;
257+
expandoPropertiesAdded?.add(expandoFunc);
258+
const type = typeChecker.getTypeAtLocation(expandoFunc);
259+
const elements = typeChecker.getPropertiesOfType(type);
260+
if (!expandoFunc.name || elements.length === 0) return undefined;
261+
const newProperties = []
262+
for (const symbol of elements) {
263+
if (!isIdentifierText(symbol.name, program.getCompilerOptions().target)) continue;
264+
newProperties.push(factory.createVariableStatement(
265+
/*modifiers*/ undefined,
266+
factory.createVariableDeclarationList(
267+
[factory.createVariableDeclaration(
268+
symbol.name,
269+
/*exclamationToken*/ undefined,
270+
typeToTypeNode(typeChecker.getTypeOfSymbol(symbol), expandoFunc),
271+
/*initializer*/ undefined,
272+
)],
273+
)
274+
));
275+
}
276+
if (newProperties.length === 0) return undefined;
277+
const modifiers: ModifierLike[] = [];
278+
if (expandoFunc.modifiers?.some((modifier)=> modifier.kind === SyntaxKind.ExportKeyword)) {
279+
modifiers.push(factory.createModifier(SyntaxKind.ExportKeyword))
280+
}
281+
modifiers.push(factory.createModifier(SyntaxKind.DeclareKeyword));
282+
const namespace = factory.createModuleDeclaration(
283+
modifiers,
284+
expandoFunc.name,
285+
factory.createModuleBlock(newProperties),
286+
/*flags*/ NodeFlags.Namespace | NodeFlags.ExportContext | NodeFlags.Ambient | NodeFlags.ContextFlags,
287+
);
288+
changeTracker.insertNodeAfter(sourceFile, expandoFunc, namespace);
289+
return [Diagnostics.Annotate_types_of_properties_expando_function_in_a_namespace];
290+
}
291+
244292
function needsParenthesizedExpressionForAssertion(node: Expression) {
245293
return !isEntityNameExpression(node) && !isCallExpression(node) && !isObjectLiteralExpression(node) && !isArrayLiteralExpression(node);
246294
}
295+
247296
function createAsExpression(node: Expression, type: TypeNode) {
248297
if (needsParenthesizedExpressionForAssertion(node)) {
249298
node = factory.createParenthesizedExpression(node);
@@ -290,7 +339,7 @@ function withChanges<T>(
290339
// We can't use typeof un an unique symbol. Would result in either
291340
// const s = Symbol("") as unique symbol
292341
// const s = Symbol("") as typeof s
293-
// both of which are not cirrect
342+
// both of which are not correct
294343
if (type && type.flags & TypeFlags.UniqueESSymbol) {
295344
return undefined;
296345
}
@@ -515,8 +564,13 @@ function withChanges<T>(
515564
const properties = typeChecker.getPropertiesOfType(targetType);
516565
if (some(properties, p => p.valueDeclaration === expandoDeclaration || p.valueDeclaration === expandoDeclaration.parent)) {
517566
const fn = targetType.symbol.valueDeclaration;
518-
if (fn && isFunctionExpressionOrArrowFunction(fn) && isVariableDeclaration(fn.parent)) {
567+
if (fn) {
568+
if (isFunctionExpressionOrArrowFunction(fn) && isVariableDeclaration(fn.parent)) {
519569
return fn.parent;
570+
}
571+
if (isFunctionDeclaration(fn)) {
572+
return fn;
573+
}
520574
}
521575
}
522576
}

tests/baselines/reference/isolated-declarations/auto-fixed/diff/declarationEmitDefaultExportWithStaticAssignment.d.ts.diff

Lines changed: 0 additions & 99 deletions
This file was deleted.

tests/baselines/reference/isolated-declarations/auto-fixed/diff/declarationEmitExpandoPropertyPrivateName.d.ts.diff

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,35 +5,18 @@
55
===================================================================
66
--- TSC declarations
77
+++ DTE declarations
8-
@@ -5,20 +5,29 @@
8+
@@ -5,8 +5,14 @@
99
}
1010
export declare function f(): I;
1111
export {};
1212
//# sourceMappingURL=a.d.ts.map
1313
+//// [b.d.ts]
1414
+export declare function q(): void;
1515
+export declare namespace q {
16-
+ var val: invalid;
16+
+ var val: I;
1717
+}
1818
+//# sourceMappingURL=b.d.ts.map
1919
/// [Errors] ////
2020

21-
b.ts(4,1): error TS4032: Property 'val' of exported interface has or is using name 'I' from private module '"a"'.
22-
+b.ts(4,1): error TS9023: Assigning properties to functions without declaring them is not supported with --isolatedDeclarations. Add an explicit declaration for the properties assigned to this function.
23-
24-
25-
==== a.ts (0 errors) ====
26-
interface I {}
27-
export function f(): I { return null as I; }
28-
-==== b.ts (1 errors) ====
29-
+==== b.ts (2 errors) ====
30-
import {f} from "./a";
31-
32-
export function q(): void {}
33-
q.val = f();
34-
~~~~~
35-
!!! error TS4032: Property 'val' of exported interface has or is using name 'I' from private module '"a"'.
36-
+ ~~~~~
37-
+!!! error TS9023: Assigning properties to functions without declaring them is not supported with --isolatedDeclarations. Add an explicit declaration for the properties assigned to this function.
38-
39-
\ No newline at end of file
21+
b.ts(5,14): error TS2304: Cannot find name 'I'.
22+
b.ts(5,14): error TS4025: Exported variable 'val' has or is using private name 'I'.

tests/baselines/reference/isolated-declarations/auto-fixed/diff/declarationEmitFunctionDuplicateNamespace.d.ts.diff

Lines changed: 0 additions & 35 deletions
This file was deleted.

0 commit comments

Comments
 (0)