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 project -
declarationMap
: 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'
Note: if your compiler has problems with the IData
file, you can add type
after the import
. See: https://dev.to/uppajung/comment/1f6bc
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!
Top comments (6)
Adding
type
afterimport
can prevent the compiler from thinking that it needs to include an IData.js file or modules that try to grab IData.js.In other words, I'd suggest changing the last code line to
That would have saved me a few hours on my electron project.
Interesting! Thanks for adding this. I'll add a link to your comment in the post.
The project I work uses the same frontend/backend/common style setup for shared types, but none of "compilerOptions" set "composite", "declarationMap" and "declaration". Are these options really required? What's the difference?
Hi Ilya, thanks for commenting.
I added
composite
,declarationMap
anddeclaration
as the docs say they are required or recommended.From the docs:
composite
(required): Referenced projects must have the new composite setting enabled. This setting is needed to ensure TypeScript can quickly determine where to find the outputs of the referenced project.declaration
is required when you usecomposite
declarationMap
(recommended): We’ve also added support for declaration source maps. If you enable --declarationMap, you’ll be able to use editor features like “Go to Definition” and Rename to transparently navigate and edit code across project boundaries in supported editors.Relevant doc sections: typescriptlang.org/docs/handbook/p...
I don't know what would happen if I removed these settings, however.
How do you solve this error? Have followed the guide properly but when calling a type from outside the SRC project I get this erorr:
./src/Components/ActionsList.tsx
Module not found: You attempted to import ../../../common/Types/Action which falls outside of the project src/ directory. Relative imports outside of src/ are not supported.
I have wrote a similar topic article
dev.to/t7yang/typescript-yarn-work...
Some comments have been hidden by the post's author - find out more