In this article, we analyse the ts-pattern usage in Documenso source code, but before that, let’s learn what a ts-pattern
library is.
ts-pattern
ts-pattern
is an exhaustive Pattern Matching library for TypeScript with smart type inference. Write better and safer conditions. Pattern matching lets you express complex conditions in a single, compact expression. Your code becomes shorter and more readable. Exhaustiveness checking ensures you haven’t forgotten any possible case.
import { match, P } from 'ts-pattern';
type Data =
| { type: 'text'; content: string }
| { type: 'img'; src: string };
type Result =
| { type: 'ok'; data: Data }
| { type: 'error'; error: Error };
const result: Result = …;
const html = match(result)
.with({ type: 'error' }, () => <p>Oups! An error occured</p>)
.with({ type: 'ok', data: { type: 'text' } }, (res) => <p>{res.data.content}</p>)
.with({ type: 'ok', data: { type: 'img', src: P.select() } }, (src) => <img src={src} />)
.exhaustive();
This example is pretty self-explanatory. Now that we understand a basic example of how ts-pattern works. Let’s apply this to the code found in Documenso.
match usage in Documenso source code.
In Documenso source code, there is a file named upload/put-file.ts and you will find this below code at line 48.
import { match } from 'ts-pattern';
import { env } from 'next-runtime-env';
export const putFile = async (file: File) => {
const NEXT_PUBLIC_UPLOAD_TRANSPORT = env('NEXT_PUBLIC_UPLOAD_TRANSPORT');
return await match(NEXT_PUBLIC_UPLOAD_TRANSPORT)
.with('s3', async () => putFileInS3(file))
.otherwise(async () => putFileInDatabase(file));
};
match
is applied against a variable named NEXT_PUBLIC_UPLOAD_TRANSPORT
. Based on the example above, you
pass a parameter to match
and run a check against it using with
. In the example from documentation above, it was based on type
and data
, but here in this code snippet from Documenso, it is a different story.
with
is checked against a string — ‘s3’. At this point, I just wanted to find out what is expected as value for the variable Next_PUBLIC_UPLOAD_TRANSPORT
. I searched for this variable — NEXT_PUBLIC_UPLOAD_TRANSPORT
across the codebase and found its type definition in a file named process-env.d.ts. This file is in package named tsconfig. Documenso is a monorepo and contains workspaces inside apps folder and packages inside packages folder.
NEXT_PUBLIC_UPLOAD_TRANSPORT?: 'database' | 's3';
NEXT_PUBLIC_UPLOAD_TRANSPORT
accepts only two string literals — ‘database’ or ‘s3’. Now it makes sense, you see. This is why you have a check against ‘s3’ string to call a function named putFileInS3
.
.with('s3', async () => putFileInS3(file))
Otherwise, the value in NEXT_PUBLIC_UPLOAD_TRANSPORT
will be ‘database’ and it handled using otherwise
:
.otherwise(async () => putFileInDatabase(file));
This reminds me of the .exhaustive()
function. ts-pattern
documentation says this about exhaustive function —
“
Exhaustiveness checking ensures you haven’t forgotten any possible case.
“
I think using .exhaustive()
when you are dealing with a match
against an object, like the one provided in documentation example, makes sense. Since this variable NEXT_PUBLIC_UPLOAD_TRANSPORT
accepts only two string literals — ‘database’ or ‘s3’, there’s only string and no object to match against, that’s why there is no need for using .exhaustive()
, instead .otherwise
is used.
About us:
At Thinkthroo, we study large open source projects and provide architectural guides. We have developed reusable Components, built with tailwind, that you can use in your project.
We offer Next.js, React and Node development services.
Book a meeting with us to discuss your project.
Top comments (0)