TypeScript, Rollup, and Shared Interfaces

Alan Mendelevich
</dev> diaries
Published in
3 min readJan 21, 2020

--

I’ve allocated up to 20 minutes to move some interfaces in a library I’m working on to a shared location (since they are used by other related library as well). More than a day later I finally managed to do it. Phew…

Pretty sure that when faced with the same issue a year from now I would spend a day trying to figure it out again. So I decided to write it down for my future self and whoever else might find it useful.

The problem

Say you have a simple TypeScript interface like this:

And a class implementing it like this:

And you have your tsconfig.json configured to emit type declarations with “declaration”: true. When you run “tsc MyClass.ts” you get an output dist directory that looks like this, as expected:

Now suppose you are building a redistributable library and use rollup to bundle it. So, you add typescript, rollup, and rollup-plugin-typescript2 dev dependencies to your project:

yarn add — dev typescript rollup rollup-plugin-typescript2

Add basic rollup configuration and run “rollup -c”. The output is still fine:

Now you decided to move that MyInterface.ts to a shared location “above” your project’s root. Your directory structure looks something like this:

You update the reference to your interface file and run rollup again. Everything goes through without a hitch. But in the dist folder you see this:

But… but… where’s is MyInterface.d.ts!? And looking for it is how I spent my day… 🤦‍♂️

The road to resolution…

I’ve googled, I’ve played with TypeScript settings, rollup config, voodoo magic and still couldn’t find the answer. At some point I thought that my maybe I ought to try to use a shared class that does something rather than just a “decorative” interface. So I’ve added a dummy MySharedClass and ran rollup again. Bam!

Error: Unexpected token (Note that you need plugins to import files that are not JavaScript)

Turns out it didn’t even understand that there was TypeScript there. Fair enough. But somehow this issue didn’t manifest itself when there was just an interface. Would have saved me hours. But anyway…

So we need to tell TypeScript that there’s another directory with TS code. Sure. Just add rootDirs to tsconfig.

“rootDirs”: [“.”, “../shared”]

Run rollup again… Yay, it went through! But…

… there’s MySharedClass.d.ts in the output but still no MyInterface.ts 😢

The Solution (or rather a workaround)

So turns out:

When importing types only, typescript hides import statements from js output and rollup doesn’t see those files and thus doesn’t send them for transpiling.

And the workaround for this is just what TypeScript’s handbook calls “import a module for side-effects only”. And a side-effect is what we are after in this case. We just “touch” that interface file in our main class (see the last line):

Run rollup again. And voila:

Find this valuable? Please consider sponsoring me on GitHub Sponsors. Thank you so much! ❤

--

--

I run AdDuplex - a cross-promotion network for Windows apps. Blog at https://blog.ailon.org. Author of "Conferences for Introverts"