DEV Community

Sid Vishnoi
Sid Vishnoi

Posted on

A Weird Hack using TypeScript

Hi! In a project in my last company, I ran into a weird issue with the modern JavaScript build system and to work around it, wrote an even weirder hack.

Sorry if you found the title being click-baity. Alternative title: Using constant assertions in TypeScript to keep constants in sync.

The Problem

The project was created using create-react-app (CRA) with TypeScript and it also had an Express server, that too in TypeScript. The project structure was simple (and an even more simplified version is shown here).

./
  src/
    App.tsx
  server/
    app.ts
Enter fullscreen mode Exit fullscreen mode

then there was a constant.

const options = ["1 day", "1 week", "2 weeks", "1 month", "1 year"];
Enter fullscreen mode Exit fullscreen mode

The constant options, was required in client side to show a list of allowed options in a <select> and the server needed it for validating the requests coming from client. Now using the DRY principle, I shouldn't be writing that same value of options in two files - as the values at two places may get out of sync. So, it needed to be shared between src and server - that's where the problem started.

Turns out, create-react-app doesn't allow imports outside src directory (See this Stack Overflow post), so I can't import options from server directory.

Hey Sid, why don't you do import from src instead?

// server/app.ts
import { options } from '../src/App.tsx';
Enter fullscreen mode Exit fullscreen mode

The build process in server was simple. Take a .ts file, and run it through tsc to create a .js file, keeping the same directory structure.

So, when I ran tsc on my server/app.ts, instead of creating a directory structure like:

./server-build/
  app.js
Enter fullscreen mode Exit fullscreen mode

it ended up creating:

./server-build/
  server/
    app.js
  src/
    app.js 
Enter fullscreen mode Exit fullscreen mode

Oops. Messing up build directory or build process just to share a constant? Not cool.

The Hack

A prior day, I came across constant assertions, a new TypeScript feature.

Turns out, in create-react-app, we can't import values outside src directory, but we can import types.

// server/app.ts
// a const assertion
const options = ["1 day", "1 week", "2 weeks", "1 month", "1 year"] as const;
// export the type instead of value
export type Options = typeof options;
Enter fullscreen mode Exit fullscreen mode
// src/App.tsx
import { Options } from '../server/app.ts';
const options: Options = ["1 day", "1 week", "2 weeks", "1 month", "1 year"];
Enter fullscreen mode Exit fullscreen mode

How does it work? Lets say, I change "2 weeks" in server to "3 weeks". What happens? Well, the client fails to compile! It fails as it's expecting options to have type Options, but "3 weeks" doesn't exist in Options at index 2. It works similarly if I change only in src.

So, we've two variables in sync using TypeScript. Not very DRY, but it works™️.

See for yourself:

Top comments (0)