• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/* @internal */
2namespace ts.codefix {
3    const fixIdPlain = "fixJSDocTypes_plain";
4    const fixIdNullable = "fixJSDocTypes_nullable";
5    const errorCodes = [Diagnostics.JSDoc_types_can_only_be_used_inside_documentation_comments.code];
6    registerCodeFix({
7        errorCodes,
8        getCodeActions(context) {
9            const { sourceFile } = context;
10            const checker = context.program.getTypeChecker();
11            const info = getInfo(sourceFile, context.span.start, checker);
12            if (!info) return undefined;
13            const { typeNode, type } = info;
14            const original = typeNode.getText(sourceFile);
15            const actions = [fix(type, fixIdPlain, Diagnostics.Change_all_jsdoc_style_types_to_TypeScript)];
16            if (typeNode.kind === SyntaxKind.JSDocNullableType) {
17                // for nullable types, suggest the flow-compatible `T | null | undefined`
18                // in addition to the jsdoc/closure-compatible `T | null`
19                actions.push(fix(checker.getNullableType(type, TypeFlags.Undefined), fixIdNullable, Diagnostics.Change_all_jsdoc_style_types_to_TypeScript_and_add_undefined_to_nullable_types));
20            }
21            return actions;
22
23            function fix(type: Type, fixId: string, fixAllDescription: DiagnosticMessage): CodeFixAction {
24                const changes = textChanges.ChangeTracker.with(context, t => doChange(t, sourceFile, typeNode, type, checker));
25                return createCodeFixAction("jdocTypes", changes, [Diagnostics.Change_0_to_1, original, checker.typeToString(type)], fixId, fixAllDescription);
26            }
27        },
28        fixIds: [fixIdPlain, fixIdNullable],
29        getAllCodeActions(context) {
30            const { fixId, program, sourceFile } = context;
31            const checker = program.getTypeChecker();
32            return codeFixAll(context, errorCodes, (changes, err) => {
33                const info = getInfo(err.file, err.start, checker);
34                if (!info) return;
35                const { typeNode, type } = info;
36                const fixedType = typeNode.kind === SyntaxKind.JSDocNullableType && fixId === fixIdNullable ? checker.getNullableType(type, TypeFlags.Undefined) : type;
37                doChange(changes, sourceFile, typeNode, fixedType, checker);
38            });
39        }
40    });
41
42    function doChange(changes: textChanges.ChangeTracker, sourceFile: SourceFile, oldTypeNode: TypeNode, newType: Type, checker: TypeChecker): void {
43        changes.replaceNode(sourceFile, oldTypeNode, checker.typeToTypeNode(newType, /*enclosingDeclaration*/ oldTypeNode, /*flags*/ undefined)!); // TODO: GH#18217
44    }
45
46    function getInfo(sourceFile: SourceFile, pos: number, checker: TypeChecker): { readonly typeNode: TypeNode, readonly type: Type } | undefined {
47        const decl = findAncestor(getTokenAtPosition(sourceFile, pos), isTypeContainer);
48        const typeNode = decl && decl.type;
49        return typeNode && { typeNode, type: checker.getTypeFromTypeNode(typeNode) };
50    }
51
52    // TODO: GH#19856 Node & { type: TypeNode }
53    type TypeContainer =
54        | AsExpression | CallSignatureDeclaration | ConstructSignatureDeclaration | FunctionDeclaration
55        | GetAccessorDeclaration | IndexSignatureDeclaration | MappedTypeNode | MethodDeclaration
56        | MethodSignature | ParameterDeclaration | PropertyDeclaration | PropertySignature | SetAccessorDeclaration
57        | TypeAliasDeclaration | TypeAssertion | VariableDeclaration;
58    function isTypeContainer(node: Node): node is TypeContainer {
59        // NOTE: Some locations are not handled yet:
60        // MappedTypeNode.typeParameters and SignatureDeclaration.typeParameters, as well as CallExpression.typeArguments
61        switch (node.kind) {
62            case SyntaxKind.AsExpression:
63            case SyntaxKind.CallSignature:
64            case SyntaxKind.ConstructSignature:
65            case SyntaxKind.FunctionDeclaration:
66            case SyntaxKind.GetAccessor:
67            case SyntaxKind.IndexSignature:
68            case SyntaxKind.MappedType:
69            case SyntaxKind.MethodDeclaration:
70            case SyntaxKind.MethodSignature:
71            case SyntaxKind.Parameter:
72            case SyntaxKind.PropertyDeclaration:
73            case SyntaxKind.PropertySignature:
74            case SyntaxKind.SetAccessor:
75            case SyntaxKind.TypeAliasDeclaration:
76            case SyntaxKind.TypeAssertionExpression:
77            case SyntaxKind.VariableDeclaration:
78                return true;
79            default:
80                return false;
81        }
82    }
83}
84