📦
Awilixで依存関係の解決を型安全に自動化する方法【TypeScript】
結論
- AwilixのloadModulesを使ってserviceの登録を自動化する
-
export default
しているファイルを収集して型定義ファイルを生成するスクリプトを実行する- この記事ではファクトリ関数のみに対応したスクリプトを紹介します
- 生成された型を
awilix.createContainer()
の型引数に渡す
// awilix-types.d.ts
import type foo from './src/foo.ts'
import type bar from './src/bar.ts'
export type Cradle = {
// importしているものをファクトリ関数と決め打っているので戻り値からserviceの型が取れる
foo: ReturnType<typeof foo>
bar: ReturnType<typeof bar>
}
// コンテナを作成するコード
const container = awilix.createContainer<Cradle>()
container.loadModules(['src/**/*.ts'], {
formatName: 'camelCase',
})
スクリプト
genAwilixTypes.ts
import { glob, mkdir, readFile, writeFile } from 'node:fs/promises'
import * as path from 'node:path'
export const generateTypes = async (args: { load: string; outDir: string }) => {
const outDirPath = path.resolve(args.outDir)
const outImports = []
const outCradles = []
for await (const rawSrcPath of glob(args.load)) {
const srcPath = path.resolve(rawSrcPath)
const src = (await readFile(srcPath)).toString()
if (!src.match(/(^|\n)export default/)) {
continue
}
const key = path.parse(srcPath).name
const srcPathFromOutFile = path.relative(outDirPath, srcPath)
outImports.push(`import type ${key} from './${srcPathFromOutFile}'`)
outCradles.push(` ${key}: ReturnType<typeof ${key}>`)
}
const outFilePath = `${outDirPath}/awilix-types.d.ts`
const content = `// generated by awilix-types
${outImports.join('\n')}
export type Cradle = {
${outCradles.join('\n')}
}
`
await mkdir(outDirPath, { recursive: true })
await writeFile(outFilePath, content)
}
おまけ: バンドラーと併用する場合
tsupの例
tsup.config.ts
import { generateTypes } from './genAwilixTypes.ts'
import { defineConfig } from 'tsup'
await generateTypes({
load: 'src/**/*.ts',
outDir: 'src/generated', // ← 生成先のディレクトリ
})
export default defineConfig(() => ({
// 省略 ...
}))
Discussion