-
-
Notifications
You must be signed in to change notification settings - Fork 162
Closed
Description
Currently our enums are generated like so:
export type Database = {
public: {
Enums: {
api_key_type: 'FOO' | 'BAR' | 'BAZ'
}
}
}
This however makes it impossible to be used in all kinds of parsers or validators, which require a value as an input, not the type, as those are stripped during compilation.
A solution to this is ti generate a type for every corresponding enum in a form of:
export const status = ['FOO', 'BAR', 'BAZ'] as const
export type status = (typeof status)[number]
and somehow expose it publicly.
Our existing `ts-morph` implementation below (click to expand)
// This script uses ts-morph to generate a "runtime enums", which are
// simply an array of all possible union types.
// The values are based on generated database enums and are required
// in order for us to be able to use those types in all kind of validators.
// It also creates additional copy of the same union type, with the same name
// and based on the array readonly values for convinience.
import { Project, SyntaxKind, VariableDeclarationKind } from 'ts-morph'
const inputFilePath = process.argv[2]
const enumsFilePath = process.argv[3]
if (!inputFilePath) {
throw new Error(`generate-runtime-enums.ts requires input path to be specified as 1st argument`)
}
if (!enumsFilePath) {
throw new Error(`generate-runtime-enums.ts requires output path to be specified as 2nd argument`)
}
console.log(`> Generating runtime enums from ${inputFilePath} at ${enumsFilePath}...`)
const project = new Project()
const originalFile = project.addSourceFileAtPath(inputFilePath)
const outputFile = project.createSourceFile(enumsFilePath, '', { overwrite: true })
const generatedEnums = originalFile
.getTypeAliasOrThrow('Database')
.getTypeNodeOrThrow()
.getFirstDescendant(
(node) => node.isKind(SyntaxKind.PropertySignature) && node.getName() === 'public'
)
?.getFirstDescendant(
(node) => node.isKind(SyntaxKind.PropertySignature) && node.getName() === 'Enums'
)
?.getDescendantsOfKind(SyntaxKind.PropertySignature)
if (!generatedEnums) {
throw new Error(
`No enums found, this should never happen; Tell Kamil he messed up and should fix it.`
)
}
for (const enumProp of generatedEnums) {
const name = enumProp.getName()
const values = enumProp
.getTypeNodeOrThrow()
.getType()
.getUnionTypes()
.map((type) => type.getLiteralValue())
.filter((value) => typeof value === 'string')
outputFile.addVariableStatement({
declarationKind: VariableDeclarationKind.Const,
declarations: [
{
name,
initializer: `[${values.map((value) => `'${value}'`).join(', ')}] as const`,
},
],
isExported: true,
})
outputFile.addTypeAlias({
name,
type: `(typeof ${name})[number]`,
isExported: true,
})
}
outputFile.saveSync()
toBeOfUse
Metadata
Metadata
Assignees
Labels
No labels