TypeScriptのtrpc使ってますか? 僕はバリバリ使ってます。
サーバー側の処理で、DBから値を取得することがあると思います。
prismaやKnexで「DATETIME型」を取得すると、TypeScript側ではDate型として認識されます。
問題
問題は、trpcでサーバー側とフロント側の型を共有する場合です。
DATETIME型の値をサーバーからフロントへ送信した段階で、TypeScriptのDate型ではなくなります。
(YYYY-MM-DDTHH:mm:ss.sssZ
の形式の、 ISOフォーマット(ISO 8601拡張フォーマット)の文字列となる)
そうすると、たとえばフロント側でYYYY年MM月DD日に変更したいとき、Date型と思って処理を行うと失敗することがあります。
Day.jsであれば、dayjs(dateValue)でISOStringだろうがDate型だろうがパースできるので大丈夫です。
しかし、date-fnsなどを使っているとDate型とISOString型で挙動が異なります。
そもそも関数の引数、返り値がstringなのかDateなのかは大きく変わってくるので、Date型で統一したいわけです。
例
import { format, parseISO } from "date-fns";
import dayjs from "dayjs";
// dayjsでYYYY-MM-DD HH:mm:ssに変換する場合は、stringでもDateでもDayjsにしてくれるのでいけるが
export const dayjsWrapperToYYYYMMDDHHmmss = (date: string | number | Date | dayjs.Dayjs) =>
dayjs().format("YYYY-MM-DD HH:mm:ss");
// date-fnsでyyyy-MM-dd HH:mm:ssに変換する
// Date型なら成功するが、ISOStringだと失敗する。ISOString型の場合はparseISOが必要。
export const dateFnsWrapperToYYYYMMDDHHmmss = (date: Date) => format(date, "yyyy-MM-dd HH:mm:ss");
どうすれば良いのか
trpcのtransformerという機能を使います。
https://trpc.io/docs/server/data-transformers
事前にsuperjsonというライブラリを追加しておきましょ。
npm i superjson
サーバー側の最低限の実装
import { initTRPC } from "@trpc/server";
import superjson from "superjson";
export const t = initTRPC.create({
transformer: superjson, //ここに足してあげる
});
export const appRouter = t.router({
getToday: t.procedure.query(() => new Date()),
});
export type AppRouter = typeof appRouter;
フロント側の実装
import "./App.css";
import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
import { httpBatchLink } from "@trpc/client";
import { useState } from "react";
import superjson from "superjson";
import { trpc } from "./utils/trpc";
import { IndexPage } from "./pages/IndexPage";
export const App = () => {
const [queryClient] = useState(() => new QueryClient());
const [trpcClient] = useState(() =>
trpc.createClient({
links: [
httpBatchLink({
url: "http://localhost:22004/trpc",
}),
],
transformer: superjson, //ここに足す!
})
);
return (
<trpc.Provider client={trpcClient} queryClient={queryClient}>
<QueryClientProvider client={queryClient}>
<IndexPage />
</QueryClientProvider>
</trpc.Provider>
);
};