Open3
変数から推論して型定義を作成する
やりたいこと
- https://zenn.dev/link/comments/92dc34dc6f8a7e で意外とあっさりできたので、他でも使えるように検証。
- 前回検証したのはたまたま元データがシンプルだったので、複雑な場合でも使いこなせるようにしたい
具体的に使えそうな場面
- コントローラーの関数の戻り値を推論して定義できれば、それを元にopenapiの値を生成したり出来そう
- コントローラーがエンティティをガバっと返却するような場合、リクエスト、レスポンスのインターフェイスを考えるのではなく、エンティティの値からリクエスト、レスポンスの型を作るような感じにしたくなる
極めたい部分
- 狙った変数を取得する
- 以前ためした時は出来た
- 狙ったところに型定義を挿入する
- これも出来た
- 狙った変数に処理を差し込む
- これも前回やった
- 狙ったアロー関数の中に処理を差し込む
- これが難しかった。アロー関数を文字にして、それを置換するのは出来たが、本当はアロー関数の中の変数を取得(ここはできた)し、その後に新たな変数などを挿入したいが、出来なかった
検証
コントローラーのclassの各メソッドの戻り値を定義したクラスを作る
- 難しい。出来なかった。
- 全部primitive型になってるはいけたが、そうでないのはJSのメソッド羅列してしまう。
- Promise解決した後の型を取るのが難しい。
import { exec } from 'child_process'
import { Project, type SourceFile } from 'ts-morph'
const project = new Project({
tsConfigFilePath: 'tsconfig.json'
})
const addTypeDefinitionFromVariable = (sourceFile: SourceFile) => {
const controllerClass = sourceFile.getClasses()[0]
if (!controllerClass) {
throw new Error('controllerClass not found')
}
const methods = controllerClass.getMethods()
const newClassFile = project.createSourceFile(
`./generated/${controllerClass.getName()}.dto.ts`
)
methods.forEach(method => {
const className = `${method.getName()}Dto`
const returnType = method.getReturnType()
const isPromise = returnType.getText().startsWith('Promise<')
if (!isPromise) {
return
}
const resolvedType = returnType.getTypeArguments()[0]
newClassFile.addClass({
isExported: true,
name: className,
properties: resolvedType.getProperties().map(property => ({
name: property.getName(),
type: property.getValueDeclarationOrThrow().getType().getText()
}))
})
// Save the new class file
newClassFile.saveSync()
})
}
const executeAll = () => {
const files = './apps/tmp.ts'
const sourceFiles = project.addSourceFilesAtPaths(files)
sourceFiles.forEach(sourceFile => {
addTypeDefinitionFromVariable(sourceFile)
sourceFile.saveSync()
})
project.saveSync()
exec(`npx prettier --write ./generated`)
}
executeAll()