参考リンク
JavaのクラスからTypeScriptの型定義を生成するtypescript-generatorを使ってみた
typescript-generator カテゴリーの記事一覧 - 毎日へっぽこ
生成例
コントローラー
@Path("/users") @EntryPoint public class UserController { private SearchUser searchUser; @Inject UserController(SearchUser searchUser) { this.searchUser = searchUser; } @GetEntry @Schema(description = "ユーザー情報を検索します") @Path("{id}") public UserResourse getUsersById(@PathParam("id") String id) { var model = searchUser.findById(UserId.of(Integer.valueOf(id))).orElseThrow(); return UserResourse.from(model); } @GetEntry @Schema(description = "ユーザー情報を全件取得します") public List<UserResourse> getUsers() { var models = searchUser.findAll(); return UserResourse.from(models); } }
レスポンスクラス
/** * UserResourse. */ @lombok.Builder(access = AccessLevel.PRIVATE) @lombok.Data public class UserResourse { private final Gender gender; private final Optional<Integer> id; private final Optional<String> name; static UserResourse from(User user) { var res = UserResourse.builder() .gender(user.getGender()) .id(Optional.ofNullable(user.getUserId().getId())) .name(user.getName().getValue()) .build(); return res; } static List<UserResourse> from(Users users) { var resList = users.getItems().stream() .map(UserResourse::from) .sorted(Comparator.comparing(res -> res.getId().get(), nullsFirst(naturalOrder()))) .collect(Collectors.toList()); return resList; } }
Enumクラス
@lombok.AllArgsConstructor @lombok.Getter public enum Gender { MALE("0"), FEMALE("1"), OTHER("2"); private final String cd; public static final EnumReverseLookup<Gender, String> byCd = new EnumReverseLookup<>(Gender.class, Gender::getCd); }
pom
<plugin> <groupId>cz.habarta.typescript-generator</groupId> <artifactId>typescript-generator-maven-plugin</artifactId> <version>${typescript-generator.version}</version> <executions> <execution> <id>typescript-generator-generate</id> <goals> <goal>generate</goal> </goals> <phase>process-classes</phase> </execution> </executions> <configuration> <jsonLibrary>jackson2</jsonLibrary> <noFileComment>true</noFileComment> <optionalPropertiesDeclaration>questionMarkAndNullableType</optionalPropertiesDeclaration> <sortTypeDeclarations>true</sortTypeDeclarations> <outputKind>module</outputKind> <outputFileType>implementationFile</outputFileType> <generateJaxrsApplicationClient>true</generateJaxrsApplicationClient> <generateJaxrsApplicationInterface>true</generateJaxrsApplicationInterface> <namespace>rest</namespace> <extensions> <extension>org.vermeerlab.plugin.typescript.generator.ext.EnumToConstConverter</extension> </extensions> <classPatterns> <classPattern>**.presentation.**</classPattern> </classPatterns> <outputFile>type-script/rest.ts</outputFile> </configuration> <dependencies> <dependency> <groupId>org.verneermlab</groupId> <artifactId>ts-generator-plugin</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </plugin>
生成されたTypeScriptファイル
export namespace rest { export interface HttpClient { request<R>(requestConfig: { method: string; url: string; queryParams?: any; data?: any; copyFn?: (data: R) => R; }): RestResponse<R>; } export interface RestApplication { /** * HTTP GET /users * Java method: ee.sample.apps.context.user.presentation.UserController.getUsers */ getUsers(): RestResponse<UserResourse[]>; /** * HTTP GET /users/{id} * Java method: ee.sample.apps.context.user.presentation.UserController.getUsersById */ getUsersById(id: string): RestResponse<UserResourse>; } export class RestApplicationClient implements RestApplication { constructor(protected httpClient: HttpClient) { } /** * HTTP GET /users * Java method: ee.sample.apps.context.user.presentation.UserController.getUsers */ getUsers(): RestResponse<UserResourse[]> { return this.httpClient.request({ method: "GET", url: uriEncoding`users` }); } /** * HTTP GET /users/{id} * Java method: ee.sample.apps.context.user.presentation.UserController.getUsersById */ getUsersById(id: string): RestResponse<UserResourse> { return this.httpClient.request({ method: "GET", url: uriEncoding`users/${id}` }); } } export interface UserResourse { gender: Gender; id?: number | null; name?: string | null; } export interface UserResourseBuilder { } export type Gender = "MALE" | "FEMALE" | "OTHER"; export type RestResponse<R> = Promise<R>; function uriEncoding(template: TemplateStringsArray, ...substitutions: any[]): string { let result = ""; for (let i = 0; i < substitutions.length; i++) { result += template[i]; result += encodeURIComponent(substitutions[i]); } result += template[template.length - 1]; return result; } // Added by 'EnumToConstConverter' extension export const Gender = { MALE: "MALE", FEMALE: "FEMALE", OTHER: "OTHER", } as const; }
org.vermeerlab.plugin.typescript.generator.ext.EnumToConstConverter
enumの変換用につくった拡張(別プロジェクトで作成して dependencyしているもののコード)
EE(Payara)で同一プロジェクトで準備をすると、起動時にエラーになるので別プロジェクトにしています(Jackson2あたりが競合している様子)
/** * Enumクラスからconstを作成します. */ public class EnumToConstConverter extends EmitterExtension { @Override public EmitterExtensionFeatures getFeatures() { var emitterExcensionFeatures = new EmitterExtensionFeatures(); emitterExcensionFeatures.generatesRuntimeCode = true; return emitterExcensionFeatures; } @Override public void emitElements(Writer writer, Settings settings, boolean exportKeyword, TsModel model) { var exportStr = exportKeyword ? "export " : ""; var tsEnums = model.getOriginalStringEnums(); tsEnums.forEach((var tsEnum) -> { writer.writeIndentedLine(""); var constName = new StringBuilder() .append(exportStr) .append("const ") .append(tsEnum.getName().getSimpleName()) .append(" = {").toString(); writer.writeIndentedLine(constName); tsEnum.getMembers().forEach((var member) -> { var memberProp = new StringBuilder() .append(settings.indentString) .append(member.getPropertyName()) .append(": \"") .append(member.getEnumValue()) .append("\",") .toString(); writer.writeIndentedLine(memberProp); }); writer.writeIndentedLine("} as const;"); }); } }
その他
コードの全量はいずれ公開予定