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 me:
Hey, my name is Ramu Narasinga. I study large open-source projects and create content about their codebase architecture and best practices, sharing it through articles, videos.
I am open to work on an interesting project. Send me an email at ramu.narasinga@gmail.com
My Github - https://github.com/ramu-narasinga
My website - https://ramunarasinga.com
My Youtube channel - https://www.youtube.com/@thinkthroo
Learning platform - https://thinkthroo.com
Codebase Architecture - https://app.thinkthroo.com/architecture
Best practices - https://app.thinkthroo.com/best-practices
Production-grade projects - https://app.thinkthroo.com/production-grade-projects
Top comments (0)