Using a Generic Service for Request Data in Effect Typescript

I am doing something weird with a "generic service" and I wonder what you think about it and if there is a more standard approach.

type RawData = string | {[k: string]: string | File };

export class RequestData extends Context.Tag('RequestData')<RequestData, RawData>(){
    /**
     * Query the request data and decode it with the provided schema.
     * The resulting Requirement is made generic over the request data.
     */
    static with = <A, I>(schema: Schema.Schema<A, I, never>) => this.pipe(
        Effect.andThen(Schema.decodeUnknown(schema)),
        a => a as Effect.Effect<A, ParseError, DetailedRequestData<I>>
    );
    /** Provide the request data to an Effect, using the input type information in the Requirement for type checking
     * This is useful for providing the request data in a test.
    */
    static provideService = <A, E, R>(
        data: R extends DetailedRequestData<infer D extends RawData> ? D : never
    ) => (effect: Effect.Effect<A, E, R>) => effect.pipe(
        Effect.provideService(RequestData, data as RawData)
    );
}

interface DetailedRequestData<_ = RawData> extends RequestData {}


I provide this service with Effect.provideService as usual when interpreting an endpoint as a Promise for Remix to consume. The endpoint is just an Effect querying some services and RequestData is one of them.

It is when I test the endpoint that I use RequestData.provideService, so that my input is type checked and auto-completed.

As a bonus I can see the input type when hovering over my endpoint. I could create an Endpoint type alias with an additional channel and make the distinction more clear between the Effect requirement, which is actually just RequestData (that's what I find confusing about the whole pattern, the generic is not part of the requirement, the effect would just fail with a ParseError), and the Endpoint expected input.

It all seems very ad hoc though
Was this page helpful?