Ever wondered if you can share interfaces, types and functions between TypeScript projects?
I'm currently developing a project consisting of two separate TypeScript applications, one being a React.js dashboard and the other an Azure Function app written in Node.js. As part of the project, the dashboard calls an API in the Azure Function app. This got me thinking, as I'm in control of both the data source and the application that uses the data, is there a way that I can share certain interfaces between the two projects?
The answer is yes, since version 3 of TypeScript you can use Project References to share code between TypeScript projects. When using Project References in my project, however, I couldn't find any official examples on how to use them - hence this post!
While the implementation below is what has worked for me, if you have any improvements, let me know in the comments.
What are Project References?
Project references allow you to structure your TypeScript programs into smaller pieces. By doing this, you can greatly improve build times, enforce logical separation between components, and organize your code in new and better ways.
How to use
Take a project that consists of a frontend and a backend written in TypeScript. Both contain an interface called IData
which is exactly the same. Currently, each time I make a change, I have to duplicate it in the other file (which is extremely annoying).
The directory of the project is:
myproject
- frontend
- app.ts
- interfaces
- IData.ts
- tsconfig.json
- backend
- server.ts
- interfaces
- IData.ts
- tsconfig.json
In order to use a single IData.ts
file between both projects, we can use Project References.
Adding the common TypeScript project
We will start by creating a third TypeScript project called common
, adding an empty tsconfig.json
file and copying the IData.ts
interface over. We can also remove it from the frontend
and backend
apps. So the directory structure will be:
myproject
- frontend
- app.ts
- tsconfig.json
- backend
- server.ts
- tsconfig.json
- common
- interfaces
- IData.ts
- tsconfig.json
This isn't enough though. In the common
app's tsconfig.json
we need to add the following:
{
"compilerOptions": {
"target": "es5", // Or whatever you want
"module": "es2015", // Or whatever you want
"declaration": true,
"declarationMap": true,
"outDir": "./dist",
"composite": true
}
}
The key parts are:
declaration
: Generates a declaration file that thefrontend
andbackend
apps can use to reference items in thecommon
app.composite
: Ensures TypeScript can quickly determine where to find the outputs of the referenced projectdeclarationMap
: Enables editor features like “Go to Definition” and Rename to transparently navigate and edit code across project boundaries in supported editors
Referencing the common project in frontend
/backend
To reference the common IData
interface in the frontend
and backend
apps we need to make a simple change to both of their tsconfig.json
files. Add the references
property to your existing tsconfig.json
.
{
"compilerOptions": {
// The usual
},
"references": [
{ "path": "../common" }
]
}
Building the frontend
/backend
apps
Now that we've added the reference to the common app in order to access its interfaces we need to compile both the frontend
and backend
apps.
When doing so, ensure you use the --build
option so that TypeScript automatically builds all referenced projects.
tsc --build .
Note: If you're using Next.js with TypeScript, I didn't need to do this. Both next dev
and next build
kept working just the same.
Importing the common interface into frontend
/backend
This is easier than you might first think, just import IData
using its relative path. TypeScript will do the magic when you compile it.
import IData from '../common/interfaces/IData'
Summary
In this post, I've demonstrated how to use TypeScript Project References to use a common project for shared interfaces, functions, types and more!
Feedback on my approach is appreciated! As I said above, I couldn't find an official example to guide me on how to use Project References so any feedback in the comments will help me improve this tutorial and my own TypeScript projects!
Thanks for reading!