Skip to content
This repository was archived by the owner on May 12, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ bin/
.vscode/*
/rewrite-javascript-remote-server/src/main/resources/node-server/node_modules/
/rewrite-javascript-remote-server/src/main/resources/node-server/package-lock.json
.DS_Store
206 changes: 122 additions & 84 deletions openrewrite/src/javascript/typeMapping.ts
Original file line number Diff line number Diff line change
@@ -1,104 +1,142 @@
import * as ts from "typescript";
import {JavaType} from "../java";
import { JavaType } from "../java";

export class JavaScriptTypeMapping {
private readonly typeCache: Map<string, JavaType> = new Map();
private readonly regExpSymbol: ts.Symbol | undefined;

private readonly typeCache: Map<string, JavaType> = new Map();
private readonly regExpSymbol: ts.Symbol | undefined;
constructor(private readonly checker: ts.TypeChecker) {
this.regExpSymbol = checker.resolveName(
"RegExp",
undefined,
ts.SymbolFlags.Type,
false
);
}

constructor(private readonly checker: ts.TypeChecker) {
this.regExpSymbol = checker.resolveName('RegExp', undefined, ts.SymbolFlags.Type, false);
type(node: ts.Node): JavaType | null {
let type: ts.Type | undefined;
if (ts.isExpression(node)) {
type = this.checker.getTypeAtLocation(node);
} else if (ts.isTypeNode(node)) {
type = this.checker.getTypeFromTypeNode(node);
}

type(node: ts.Node): JavaType | null {
let type: ts.Type | undefined;
if (ts.isExpression(node)) {
type = this.checker.getTypeAtLocation(node);
} else if (ts.isTypeNode(node)) {
type = this.checker.getTypeFromTypeNode(node);
}
return type ? this.getType(type) : null;
}

return type ? this.getType(type) : null;
private getType(type: ts.Type) {
const signature = this.getSignature(type);
const existing = this.typeCache.get(signature);
if (existing) {
return existing;
}
const result = this.createType(type, signature);
this.typeCache.set(signature, result);
return result;
}

private getType(type: ts.Type) {
const signature = this.getSignature(type);
const existing = this.typeCache.get(signature);
if (existing) {
return existing;
}
const result = this.createType(type, signature);
this.typeCache.set(signature, result);
return result;
}
private getSignature(type: ts.Type) {
// FIXME for classes we need to include the containing module / package in the signature and probably include in the qualified name
return this.checker.typeToString(type);
}

primitiveType(node: ts.Node): JavaType.Primitive {
const type = this.type(node);
return type instanceof JavaType.Primitive
? type
: JavaType.Primitive.of(JavaType.PrimitiveKind.None);
}

private getSignature(type: ts.Type) {
// FIXME for classes we need to include the containing module / package in the signature and probably include in the qualified name
return this.checker.typeToString(type);
variableType(node: ts.NamedDeclaration): JavaType.Variable | null {
if (ts.isVariableDeclaration(node)) {
const symbol = this.checker.getSymbolAtLocation(node.name);
if (symbol) {
const type = this.checker.getTypeOfSymbolAtLocation(symbol, node);
}
}
return null;
}

methodType(node: ts.Node): JavaType.Method | null {
return null;
}

primitiveType(node: ts.Node): JavaType.Primitive {
const type = this.type(node);
return type instanceof JavaType.Primitive ? type : JavaType.Primitive.of(JavaType.PrimitiveKind.None);
private createType(type: ts.Type, signature: string): JavaType {
if (type.isLiteral()) {
if (type.isNumberLiteral()) {
return JavaType.Primitive.of(JavaType.PrimitiveKind.Double);
} else if (type.isStringLiteral()) {
return JavaType.Primitive.of(JavaType.PrimitiveKind.String);
}
}

variableType(node: ts.NamedDeclaration): JavaType.Variable | null {
if (ts.isVariableDeclaration(node)) {
const symbol = this.checker.getSymbolAtLocation(node.name);
if (symbol) {
const type = this.checker.getTypeOfSymbolAtLocation(symbol, node);
}
}
return null;
if (type.flags === ts.TypeFlags.Null) {
return JavaType.Primitive.of(JavaType.PrimitiveKind.Null);
} else if (type.flags === ts.TypeFlags.Undefined) {
return JavaType.Primitive.of(JavaType.PrimitiveKind.None);
} else if (
type.flags === ts.TypeFlags.Number ||
type.flags === ts.TypeFlags.NumberLiteral ||
type.flags === ts.TypeFlags.NumberLike
) {
return JavaType.Primitive.of(JavaType.PrimitiveKind.Double);
} else if (
type.flags === ts.TypeFlags.String ||
type.flags === ts.TypeFlags.StringLiteral ||
type.flags === ts.TypeFlags.StringLike
) {
return JavaType.Primitive.of(JavaType.PrimitiveKind.String);
} else if (type.flags === ts.TypeFlags.Void) {
return JavaType.Primitive.of(JavaType.PrimitiveKind.Void);
} else if (
type.flags === ts.TypeFlags.BigInt ||
type.flags === ts.TypeFlags.BigIntLiteral ||
type.flags === ts.TypeFlags.BigIntLike
) {
return JavaType.Primitive.of(JavaType.PrimitiveKind.Long);
} else if (
(type.symbol !== undefined && type.symbol === this.regExpSymbol) ||
this.checker.typeToString(type) === "RegExp"
) {
return JavaType.Primitive.of(JavaType.PrimitiveKind.String);
}

methodType(node: ts.Node): JavaType.Method | null {
return null;
/**
* TypeScript may assign multiple flags to a single type (e.g., Boolean + Union).
* Using a bitwise check ensures we detect Boolean even if other flags are set.
*/
if (
type.flags & ts.TypeFlags.Boolean ||
type.flags & ts.TypeFlags.BooleanLiteral ||
type.flags & ts.TypeFlags.BooleanLike
) {
return JavaType.Primitive.of(JavaType.PrimitiveKind.Boolean);
}

private createType(type: ts.Type, signature: string): JavaType {
if (type.isLiteral()) {
if (type.isNumberLiteral()) {
return JavaType.Primitive.of(JavaType.PrimitiveKind.Double);
} else if (type.isStringLiteral()) {
return JavaType.Primitive.of(JavaType.PrimitiveKind.String);
}
}

if (type.flags === ts.TypeFlags.Null) {
return JavaType.Primitive.of(JavaType.PrimitiveKind.Null);
} else if (type.flags === ts.TypeFlags.Undefined) {
return JavaType.Primitive.of(JavaType.PrimitiveKind.None);
} else if (type.flags === ts.TypeFlags.Number) {
return JavaType.Primitive.of(JavaType.PrimitiveKind.Double);
} else if (type.flags === ts.TypeFlags.String) {
return JavaType.Primitive.of(JavaType.PrimitiveKind.String);
} else if (type.flags === ts.TypeFlags.BooleanLiteral) {
return JavaType.Primitive.of(JavaType.PrimitiveKind.Boolean);
} else if (type.flags === ts.TypeFlags.Void) {
return JavaType.Primitive.of(JavaType.PrimitiveKind.Void);
} else if (type.symbol === this.regExpSymbol) {
return JavaType.Primitive.of(JavaType.PrimitiveKind.String);
}

if (type.isUnion()) {
let result = new JavaType.Union();
this.typeCache.set(signature, result);

const types = type.types.map(t => this.getType(t));
result.unsafeSet(types);
return result;
} else if (type.isClass()) {
// FIXME flags
let result = new JavaType.Class(0, type.symbol.name, JavaType.Class.Kind.Class);
this.typeCache.set(signature, result);
// FIXME unsafeSet
return result;
}

// if (ts.isRegularExpressionLiteral(node)) {
// return JavaType.Primitive.of(JavaType.PrimitiveKind.String);
// }
return JavaType.Unknown.INSTANCE;
if (type.isUnion()) {
let result = new JavaType.Union();
this.typeCache.set(signature, result);

const types = type.types.map((t) => this.getType(t));
result.unsafeSet(types);
return result;
} else if (type.isClass()) {
// FIXME flags
let result = new JavaType.Class(
0,
type.symbol.name,
JavaType.Class.Kind.Class
);
this.typeCache.set(signature, result);
// FIXME unsafeSet
return result;
}
}

// if (ts.isRegularExpressionLiteral(node)) {
// return JavaType.Primitive.of(JavaType.PrimitiveKind.String);
// }

return JavaType.Unknown.INSTANCE;
}
}
Loading
Loading