I finally found my first use case for a functor.
Here’s my goal. Remix has a loader
function and a useLoaderData
function. With TypeScript you add in a typeof loader
to the useLoaderData
and the response from that hook is typed correctly.
import { useLoaderData, json } from "@remix-run/react";
import { prisma } from "../db";
export async function loader() {
return json(await prisma.user.findMany());
}
export default function Users() {
const data = useLoaderData<typeof loader>();
return (
<ul>
{data.map((user) => (
<li key={user.id}>{user.name}</li>
))}
</ul>
);
}
It’s almost exactly the same as the return type of json
except it strips out the promises
from the types, so instead of promise<user>
we just get user
.
Here’s what I have so far in ReScript.
// Remix.res
module Loader = {
type json<'a> = 'a
@module("@remix-run/react")
external json: {..} => json<'a> = "json"
type t<'a> = unit => promise<'a>
}
module type LoaderData = {
type t
}
module MakeLoader = (Data: LoaderData) => {
type json = Loader.json<Data.t>
type t = Loader.t<Data.t>
@module("@remix-run/react")
external useLoaderData: unit => Data.t = "useLoaderData"
}
// _index.res
open Webapi
let headers: Remix.Headers.t = (
~_actionHeaders,
~_errorHeaders,
~_loaderHeaders,
~_parentHeaders,
) =>
{
"Cache-Control": "max-age=300, s-maxage=3600",
}
module Data = {
type t = {"foo": string, "data": array<string>}
}
module Loader = Remix.MakeLoader(Data)
let loader: Loader.t = async () => {
Remix.Loader.json({
"foo": "bar",
"data": await Fetch.fetch("https://baconipsum.com/api/?type=meat-and-filler")->Promise.then(
Fetch.Response.json,
),
})
}
@react.component
let make = () => {
let data = Loader.useLoaderData() // type here is correct: {"foo": string, "data": array<string>}
<>
<Home_hero />
</>
}
This is pretty good, but I would really love it if I could have type checking work across what is being passed to json
and the type returned from useLoaderData
.
In this example I would need {"foo": string, "data": array<string>}
to somehow match up to {"foo": string, "data": promise<array<string>>}
.
I’m hoping someone has a clever idea.