effect icon indicating copy to clipboard operation
effect copied to clipboard

Effect Schema TypeScript Code Generation

Open cyberixae opened this issue 7 months ago • 2 comments

What is the problem this feature would solve?

I tend to work in environments where schemas already exist in one form or another ( JSON Schema, GraphQL Schema, ... ). I'd like to generate Effect Schemas based on the existing schema definitions.

What is the feature you are proposing to solve the problem?

I'm thinking about something similar to io-ts-codegen which enabled me to write io-ts-from-json-schema package for converting JSON schemas to io-ts codecs. Note that TypeScript compiler will give up inferring types for large generated code bases without explicit type assertions microsoft/TypeScript/issues/40354 Generating type assertions is therefore also necessary in addition to generating the JavaScript code.

Consider the following simplified code generation example. It takes an arbitrary JSON structure as input and constructs a schema to match the input. This conversion is of course not accurate since there are multiple possible schemas for the same JSON structure. However, it should be good enough for demonstrating code generation.

import type { JsonValue } from "fast-check"
import { JSONSchema, Match, Schema, Record } from "effect"

const guessSchema = (value?: JsonValue): Schema.Schema.Any =>
  Match.value(value).pipe(
    Match.when(Match.undefined, () => Schema.Unknown),
    Match.when(Match.null, () => Schema.Null),
    Match.when(Match.string, () => Schema.String),
    Match.when(Match.number, () => Schema.Number),
    Match.when(Match.boolean, () => Schema.Boolean),
    Match.when(Array.isArray, ([item]) => Schema.Array(guessSchema(item))),
    Match.when(Match.record, (record) =>
      Schema.Struct(Record.map(record, guessSchema))
    ),
    Match.exhaustive
  )

// @ts-expect-error Not implemented
const tsSchema = TypeScript.make(guessSchema({ name: "John", age: 30 }))
/*
Output:
`import { Schema } from "effect"

type Default = Schema.Struct<{
  name: typeof Schema.String
  age: typeof Schema.Number
}>
const Default: Default = Schema.Struct({
  name: Schema.String,
  age: Schema.Number
})

export default Default`
*/

The TypeScript.make function in the example would solve my problem. I could then implement custom code generation utilities as needed. Effect could perhaps additionally provide an out of the box conversion utility for JSON Schema, since Effect already has some builtin JSON Schema support.

What alternatives have you considered?

Using the existing JSON Schema generator and an existing JSON schema validation library should also work. However, that would probably be less accurate due to double conversion.

const jsonSchema = JSONSchema.make(guessSchema({ name: "John", age: 30 }))
/*
Output:
{
  "$schema": "http://json-schema.org/draft-07/schema#",
  "type": "object",
  "required": [
    "name",
    "age"
  ],
  "properties": {
    "name": {
      "type": "string"
    },
    "age": {
      "type": "number"
    }
  },
  "additionalProperties": false
}
*/

cyberixae avatar May 12 '25 11:05 cyberixae

Hey. Have you seen https://app.quicktype.io/ ?

fubhy avatar May 12 '25 13:05 fubhy

At least I didn't know/remember QuickType supported Effect Schema. Worth checking out. Might be good for some use cases.

QuickType seems to be using hardcoded strings for generating Effect source code glideapps/quicktype@472075a

cyberixae avatar May 12 '25 15:05 cyberixae