Repository: babel/babel (TypeScript)

Issue:
[Bug]: Typescript mapped type with implicit any results in faulty TS
### 💻

- [ ] Would you like to work on a fix?

### How are you using Babel?

Programmatic API (`babel.transform`, `babel.parse`)

### Input code

```ts
type Bar = { [key in Foo] };
```

### Configuration file name

_No response_

### Configuration

options passed to @babel/parser:
```js
{
    sourceType: 'module',
    plugins: ['typescript'],
}
```

### Current and expected behavior

When just parsed using @babel/parser and generated back using @babel/generator, with nothing else being done to AST, the mentioned code becomes:
```ts
type Bar = { [key in Foo]: };
```
which is an ivalid typescript, raises "Type expected" error because indeed, there is no type after `:`.

Expected behavior:
It should be generated back into
```ts
type Bar = { [key in Foo] };
```

### Environment

- Babel: 7.24.3
- @babel/plugin-syntax-typescript: 7.24.1
- Node: 18.19.0
- npm: 10.2.3

### Possible solution

If the `MappedType` node has its `type` field set to `undefined`, there should be no `:` printed out after the `[key in Foo]`.

### Additional context

_No response_


Hints:
Hey @aczekajski! We really appreciate you taking the time to report an issue. The collaborators on this project attempt to help as many people as possible, but we're a limited number of volunteers, so it's possible this won't be addressed swiftly.

If you need any help, or just have general Babel or JavaScript questions, we have a vibrant [Slack community](https://babeljs.slack.com) that typically always has someone willing to help. You can sign-up [here](https://slack.babeljs.io/) for an invite.

Patch:
diff --git a/packages/babel-generator/src/generators/typescript.ts b/packages/babel-generator/src/generators/typescript.ts
--- a/packages/babel-generator/src/generators/typescript.ts
+++ b/packages/babel-generator/src/generators/typescript.ts
@@ -414,7 +414,7 @@ export function TSIndexedAccessType(
 }
 
 export function TSMappedType(this: Printer, node: t.TSMappedType) {
-  const { nameType, optional, readonly, typeParameter } = node;
+  const { nameType, optional, readonly, typeParameter, typeAnnotation } = node;
   this.token("{");
   this.space();
   if (readonly) {
@@ -443,13 +443,15 @@ export function TSMappedType(this: Printer, node: t.TSMappedType) {
 
   this.token("]");
 
-  if (optional) {
-    tokenIfPlusMinus(this, optional);
-    this.token("?");
+  if (typeAnnotation) {
+    if (optional) {
+      tokenIfPlusMinus(this, optional);
+      this.token("?");
+    }
+    this.token(":");
+    this.space();
+    this.print(typeAnnotation, node);
   }
-  this.token(":");
-  this.space();
-  this.print(node.typeAnnotation, node);
   this.space();
   this.token("}");
 }


Test patch:
diff --git a/packages/babel-generator/test/fixtures/typescript/mapped-type/input.ts b/packages/babel-generator/test/fixtures/typescript/mapped-type/input.ts
new file mode 100644
--- /dev/null
+++ b/packages/babel-generator/test/fixtures/typescript/mapped-type/input.ts
@@ -0,0 +1 @@
+type Bar = { [key in Foo] };
diff --git a/packages/babel-generator/test/fixtures/typescript/mapped-type/output.js b/packages/babel-generator/test/fixtures/typescript/mapped-type/output.js
new file mode 100644
--- /dev/null
+++ b/packages/babel-generator/test/fixtures/typescript/mapped-type/output.js
@@ -0,0 +1 @@
+type Bar = { [key in Foo] };
\ No newline at end of file