From 96fe153679f72c7eb94a826c70f4644905015eca Mon Sep 17 00:00:00 2001 From: Andrii Rodionov Date: Fri, 22 Nov 2024 21:28:40 +0100 Subject: [PATCH] Implemented TupleType, OptionalType, RestType, TypeOperator --- openrewrite/src/javascript/parser.ts | 49 +++++++++- .../test/javascript/parser/function.test.ts | 2 +- .../test/javascript/parser/typeAlias.test.ts | 90 ++++++++++++++++++- 3 files changed, 132 insertions(+), 9 deletions(-) diff --git a/openrewrite/src/javascript/parser.ts b/openrewrite/src/javascript/parser.ts index d883e465..e563bddc 100644 --- a/openrewrite/src/javascript/parser.ts +++ b/openrewrite/src/javascript/parser.ts @@ -1270,15 +1270,41 @@ export class JavaScriptParserVisitor { } visitTupleType(node: ts.TupleTypeNode) { - return this.visitUnknown(node); + return new JS.Tuple( + randomId(), + this.prefix(node), + Markers.EMPTY, + new JContainer( + Space.EMPTY, + node.elements.length > 0 ? + node.elements.map(p => this.rightPadded(this.convert(p), this.suffix(p))) + .concat(node.elements.hasTrailingComma ? this.rightPadded(this.newJEmpty(), this.prefix(this.findChildNode(node, ts.SyntaxKind.CloseBracketToken)!)) : []) + : [this.rightPadded(this.newJEmpty(this.prefix(this.findChildNode(node, ts.SyntaxKind.CloseBracketToken)!)), Space.EMPTY)], // to handle the case: [/*no*/] + Markers.EMPTY), + this.mapType(node) + ); } visitOptionalType(node: ts.OptionalTypeNode) { - return this.visitUnknown(node); + return new JS.Unary( + randomId(), + Space.EMPTY, + Markers.EMPTY, + this.leftPadded(this.suffix(node.type), JS.Unary.Type.Optional), + this.visit(node.type), + this.mapType(node) + ); } visitRestType(node: ts.RestTypeNode) { - return this.visitUnknown(node); + return new JS.Unary( + randomId(), + this.prefix(node), + Markers.EMPTY, + this.leftPadded(Space.EMPTY, JS.Unary.Type.Spread), + this.convert(node.type), + this.mapType(node) + ); } visitUnionType(node: ts.UnionTypeNode) { @@ -1318,7 +1344,22 @@ export class JavaScriptParserVisitor { } visitTypeOperator(node: ts.TypeOperatorNode) { - return this.visitUnknown(node); + function mapTypeOperator(operator: ts.SyntaxKind.KeyOfKeyword | ts.SyntaxKind.UniqueKeyword | ts.SyntaxKind.ReadonlyKeyword): JS.TypeOperator.Type | undefined { + switch (operator) { + case ts.SyntaxKind.KeyOfKeyword: + return JS.TypeOperator.Type.KeyOf; + case ts.SyntaxKind.ReadonlyKeyword: + return JS.TypeOperator.Type.ReadOnly; + } + } + + return new JS.TypeOperator( + randomId(), + this.prefix(node), + Markers.EMPTY, + mapTypeOperator(node.operator)!, + this.leftPadded(this.prefix(node.type), this.visit(node.type)) + ); } visitIndexedAccessType(node: ts.IndexedAccessTypeNode) { diff --git a/openrewrite/test/javascript/parser/function.test.ts b/openrewrite/test/javascript/parser/function.test.ts index 89207288..cf8767d3 100644 --- a/openrewrite/test/javascript/parser/function.test.ts +++ b/openrewrite/test/javascript/parser/function.test.ts @@ -260,7 +260,7 @@ describe('function mapping', () => { ); }); - test.skip('function with multiple default types', () => { + test('function with multiple default types', () => { rewriteRun( //language=typescript typeScript(` diff --git a/openrewrite/test/javascript/parser/typeAlias.test.ts b/openrewrite/test/javascript/parser/typeAlias.test.ts index 8f968d8e..574a1643 100644 --- a/openrewrite/test/javascript/parser/typeAlias.test.ts +++ b/openrewrite/test/javascript/parser/typeAlias.test.ts @@ -94,21 +94,103 @@ describe('type alias mapping', () => { ); }); - test.skip('conditional type', () => { + test('construct function type alias with generic', () => { rewriteRun( //language=typescript typeScript(` - type Flatten = T extends (infer R)[] ? Flatten : T; + type GenericConstructor = new (/*a*/.../*b*/args: any[]) => T; `) ); }); - test('construct function type alias with generic', () => { + test('tuple type', () => { rewriteRun( //language=typescript typeScript(` - type GenericConstructor = new (/*a*/.../*b*/args: any[]) => T; + type MyTuple = [number, string, boolean]; + `) + ); + }); + + test('tuple type with comments', () => { + rewriteRun( + //language=typescript + typeScript(` + type MyTuple = /*a*/[/*b*/number/*c*/, /*d*/string/*e*/, /*f*/boolean/*g*/, /*h*/]/*j*/; + `) + ); + }); + + test('tuple type empty', () => { + rewriteRun( + //language=typescript + typeScript(` + type MyTuple = [/*a*/]; + `) + ); + }); + + test('nested tuple type', () => { + rewriteRun( + //language=typescript + typeScript(` + type NestedTuple = [number, [string, boolean]]; + `) + ); + }); + + test('optional tuple type', () => { + rewriteRun( + //language=typescript + typeScript(` + type OptionalTuple = [string, /*a*/number/*b*/?/*c*/]; + `) + ); + }); + + test('tuple rest type', () => { + rewriteRun( + //language=typescript + typeScript(` + type FlexibleTuple = [string, ...number[]]; + `) + ); + }); + + test('readonly operator tuple type', () => { + rewriteRun( + //language=typescript + typeScript(` + type ReadonlyTuple = readonly [string, number]; + `) + ); + }); + + test('readonly operator tuple type with comments', () => { + rewriteRun( + //language=typescript + typeScript(` + type ReadonlyTuple = /*a*/keyof /*b*/ [string, number]; + `) + ); + }); + + test.skip('basic conditional type', () => { + rewriteRun( + //language=typescript + typeScript(` + type IsString = T extends string ? "Yes" : "No"; + `) + ); + }); + + test.skip('conditional type', () => { + rewriteRun( + //language=typescript + typeScript(` + type Flatten = T extends (infer R)[] ? Flatten : T; `) ); }); + });