There is this new feature in TypeScript 3.0 called Project References. It suggests better interaction of *.ts
modules between themselves. Unfortunately, this is all I could get from the official documentation 😞 although it seems to be written pretty clearly and straightforward.
Can anyone help me understand exactly, what problems does it solve, how does it do that, and how would I benefit from it? I have a project with a similar structure, so it might (or might not) be very helpful for it. Thank you in advance!
UPD: The project structure is roughly:
project/ lib/ index.ts # defines the original code test/ index.spec.ts # requires lib/index.ts package.json tsconfig.json
A reference project is a write-protected copy of a project at a particular time.
Project references are a new feature in TypeScript 3.0 that 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.
The feature allows defining parts of the project as separate TypeScript modules. Among other things, this allows configuring those modules differently, building them separately, etc.
Initially, the project structure, when simplified, is similar to this:
/ src/ entity.ts # exports an entity test/ entity.spec.ts # imports an entity tsconfig.json
An entity is defined in src/entity.ts
module, and then used in test/entity.spec.ts
file.
Notice that there is only one tsconfig.json
file here, sitting in the root folder. This basically says that this folder contains one big solid TypeScript project. This project includes a couple of files, organized in folders; some of those files are used for testing other ones.
This structure however imposes a problem: the process of compiling the project (namely, tsc
) also compiles the test files, thus creating dist/test/entity.spec.{js|d.ts}
files in the output. This should not happen, therefore the tsconfig.json
file is slightly altered to include only those files/folders that are intended for outside usage:
{ "compilerOptions": { // compiler options }, "include": [ "./src" ] }
This solves the problem, but in my case, it also led to all files in the /test
folder being occasionally ignored by the TypeScript compiler during the development process. Also, this exclusive approach might not fit everybody.
After utilizing the feature, the project structure has changed to this:
/ src/ entity.ts # exports an entity tsconfig.json test/ entity.spec.ts # imports an entity tsconfig.json tsconfig-base.json
Let's go through the changes:
/tsconfig.json
to /tsconfig-base.json
is a pretty major thing by itself: the root folder is not a TypeScript project anymore, since tsc
requires the tsconfig.json
file to be present.src/tsconfig.json
and test/tsconfig.json
files turns both src
and test
into two separate TypeScript projects, independent from each other.The contents of /{src|test}/tsconfig.json
files are similar since no changes in the configuration were expected, i.e., the "strictness", the output folder, as well as other such parameters, should be preserved. In order to make them similar without copy-pasting anything, all the configurations are put in an arbitrary file, accessible from both places; in this case, the tsconfig-base.json
in the root folder was selected for that:
// the contents of /tsconfig-base.json { "compilerOptions": { // compiler options, common to both projects } }
This file is being "inherited" then by /{src|test}/tsconfig.json
files, with addition of any other options if needed:
// the contents of /{src|test}/tsconfig.json { "extends": "../tsconfig-base.json", "compilerOptions": { // additional compiler options, specific to a project } }
Notice how this pattern is similar to defining an
abstract class
with incomplete implementation, and then extending it by two separate "concrete" classes.
Now, /src
and /test
folders basically hold two separate TypeScript projects with similar configurations. The last thing to do is to specify the relation between the two. Since test
depends on src
, the test
has to somehow "know" about src
. This is done in two pretty obvious steps:
allow src
to be "referenced" from the outside by declaring it as "composite":
// in /src/tsconfig.json { "extends": "../tsconfig-base.json", "compilerOptions": { // compiler options "composite": true } }
reference src
from test
:
// in /test/tsconfig.json { "extends": "../tsconfig-base.json", "references": [ { "path": "../src" } ] }
The "include"
array in /tsconfig-base.json
is not needed now, since the code exclusion is done by "drawing new borders".
UPDATE: the following section seems to be outdated since TypeScript 3.7
Now, the test
project requires *.d.ts
files for the src
project to be present. This means that before running tests, the src
should already be built, separately. This is done by using the new mode of tsc
, triggered by the --build
option:
tsc --build src
This command builds the src
project and puts the output in the specified output folder (in this case, /dist
), without neither breaking test
nor losing any compile errors.
It's for TypeScript libraries you develop, that are used by other TypeScript application. So for example, if you make some util library like lodash
but are actively developing it along side your dependent application, the references
in ``tsconfig.json``` lets you reference the source code, and have your dependent application be rebuilt automatically when the util source changes (i.e.: tsc
detects source code changes in the util ts lib)
In my case specifically, I use the references
in conjunction with npm link
and git submodules
and it's working out a lot better than in the ts 2.x
days.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With