Repository: nicoespeon/abracadabra (ts)

Issue:
Split declaration and initialization duplicates type in ts declarations
## Describe the bug

For variable declarations in Typescript, type annotations are duplicated by the "split declaration and initialization" refactoring.

## How to reproduce

1. Initial code:
```ts
let index: number = 0;
```

2.  Apply "split declaration and initialization" refactoring.

3. Output:
```ts
let index: number;
index: number = 0;
```

## Expected behavior

Should be only one type annotation:
```ts
let index: number;
index = 0;
```

## Investigation & Solution
The AST hierarchy for type annotations looks like this:
```js
{
  type: 'VariableDeclaration',
  declarations: [
    {
      type: 'VariableDeclarator',
      id: {
        type: 'Identifier',
        typeAnnotation: { // <= Shouldn't be copied
          type: 'TSTypeAnnotation',
          typeAnnotation: 'TSNumberKeyword'
        }
      }
    }
  ]
}
```

The `typeAnnotation` field on the `Identifier` node is important.

When an `ExpressionStatement` node is created with the `Identifier` copied from the declaration, the `typeAnnotation` field on `Identifier` should not be copied too.

Note that for `Identifiers` without annotation, the `typeAnnotation` field is not set to `undefined`, but is undeclared. So, the `typeAnnotation` field shouldn't be set to `undefined` when copied, but deleted instead.

A possible solution is to delete the `typeAnnotation` field after the Identifier node is copied.

## Additional information

- Version of the extension impacted: 4.13.1

- Note that the AST explorer doesn't support Typescript, so to look at the ASL for Typescript code I made a set up like this:
```js
import parser from "@babel/parser";

const code = `let index: number = 0;`;

const ast = parser.parse(code, {
  plugins: ["typescript"]
});

console.log(ast.program.body[0].declarations[0].id)
```

Pull request:
Split declaration initialization / Fix TS Annotation Duplication
Closes #232 

Adds a test to cover split declaration with TS annotation and fixes the duplication issue.

![split-declaration-initialization-ts](https://user-images.githubusercontent.com/53665722/107094724-8b6fff80-67d5-11eb-8471-baeba0f3ee70.gif)

I'm not sure if my implementation is readable. I felt the need to add a comment to clarify why I create a new Identifier.
It's necessary, because if I reassign the `typeAnnotation` property to null, it mutates the Identifier node in the `variableDeclaration` too. I want the annotation to remain in the declaration, so I need to create a new Identifier and modify that.

Feel free to improve the implementation!


Patch:
diff --git a/src/refactorings/split-declaration-and-initialization/split-declaration-and-initialization.ts b/src/refactorings/split-declaration-and-initialization/split-declaration-and-initialization.ts
index a7ae3e77..4a38630c 100644
--- a/src/refactorings/split-declaration-and-initialization/split-declaration-and-initialization.ts
+++ b/src/refactorings/split-declaration-and-initialization/split-declaration-and-initialization.ts
@@ -32,9 +32,12 @@ function updateCode(ast: t.AST, selection: Selection): t.Transformed {
         ),
         ...declarations
           .filter(isDeclarationInitialized)
-          .map(({ id, init }) =>
-            t.expressionStatement(t.assignmentExpression("=", id, init))
-          )
+          .map(function ({ id, init }) {
+            if (id.type == "Identifier" && "typeAnnotation" in id) {
+              id = t.identifier(id.name); // new identifier without annotation
+            }
+            return t.expressionStatement(t.assignmentExpression("=", id, init));
+          })
       ]);
     })
   );


Test patch:
diff --git a/src/refactorings/split-declaration-and-initialization/split-declaration-and-initialization.test.ts b/src/refactorings/split-declaration-and-initialization/split-declaration-and-initialization.test.ts
index 03c40c10..2ff3a0b4 100644
--- a/src/refactorings/split-declaration-and-initialization/split-declaration-and-initialization.test.ts
+++ b/src/refactorings/split-declaration-and-initialization/split-declaration-and-initialization.test.ts
@@ -60,6 +60,13 @@ lastName = "Doe";`
         expected: `let firstName, lastName, age;
 firstName = "Jane";
 lastName = "Doe";`
+      },
+      {
+        description: "declarations with type annotations",
+        code: `const firstName: string = "Jane", age: number = 90;`,
+        expected: `let firstName: string, age: number;
+firstName = "Jane";
+age = 90;`
       },
       {
         description: "nested declaration, cursor on wrapper",