Open3

変数から推論して型定義を作成する

astatsuyaastatsuya

やりたいこと

  • https://zenn.dev/link/comments/92dc34dc6f8a7e で意外とあっさりできたので、他でも使えるように検証。
  • 前回検証したのはたまたま元データがシンプルだったので、複雑な場合でも使いこなせるようにしたい

具体的に使えそうな場面

  • コントローラーの関数の戻り値を推論して定義できれば、それを元にopenapiの値を生成したり出来そう
    • コントローラーがエンティティをガバっと返却するような場合、リクエスト、レスポンスのインターフェイスを考えるのではなく、エンティティの値からリクエスト、レスポンスの型を作るような感じにしたくなる
astatsuyaastatsuya

極めたい部分

  • 狙った変数を取得する
    • 以前ためした時は出来た
  • 狙ったところに型定義を挿入する
    • これも出来た
  • 狙った変数に処理を差し込む
    • これも前回やった
  • 狙ったアロー関数の中に処理を差し込む
    • これが難しかった。アロー関数を文字にして、それを置換するのは出来たが、本当はアロー関数の中の変数を取得(ここはできた)し、その後に新たな変数などを挿入したいが、出来なかった
astatsuyaastatsuya

検証

コントローラーの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()