Cross-project references between two projects

Nickon Source

Is it possible to make a reference between two TypeScript projects? Assume we have the following project structure:

Structure

Module1.ts contains:

module TestModule {
    export interface Interface1 {
    }
}

Module2.ts contains:

module TestModule {
    export interface Interface2 extends Interface1 {
    }
}

Test1 is referenced in Test2. I get an error Could not find symbol 'Interface1' in Module2.ts. It works within one project, but I don't know how to make it visible from the other project... Maybe it's not possible for now.

[Edit 1.]
When I try to use TestModule.Interface1 pattern, I get the same error (said in different way). But the IntelliSense sees my Interface1:

IntelliSense

[Edit 2.]
I have noticed I can't use files from the other project. Even if I have a correct reference (/// <reference ...) added and linked all the files in my 1st project.

visual-studioreferencetypescript

Answers

answered 4 years ago basarat #1

If you are compiling with the --out parameter you can simply reference Module1.ts from Module2.ts using /// <reference To learn more about code organization patterns in TypeScript see http://www.youtube.com/watch?v=KDrWLMUY0R0&hd=1

What visual studio language services sees available (which is everything) is different from what you compile and actually have available at runtime.

answered 4 years ago David Sherret #2

There's lots of ways you can do this.

Option 1 - Project References (TypeScript 3.0+)

If you are using TypeScript 3.0 then look at using project references. Read more here: https://blogs.msdn.microsoft.com/typescript/2018/07/12/announcing-typescript-3-0-rc/

Option 2 - Build script

Use something like gulp-typescript, grunt-ts, or just a batch script to copy the files over into a folder in the main project.

Alternatively, run a build event in Visual Studio that will copy the files over to the main project.

Option 3 - npm package

If you use npm, you could create a package for your other project. Then you can use your package in your main project. Specifying a local dependency is a good way to do this or by using something like sinopia, which is a private repository server. I've never used it, but it looks like it would work well.

Option 4 - NuGet package

You could look into creating a nuget package and then installing it locally.

Option 5 - --declaration --outDir compiler option

You can set the --outDir compiler option or the outDir property in tsconfig.json with the directory to your other project then also compile it with --declaration so that it generates the declaration files (.d.ts) too. For example: --declaration --outDir ../Test1/External.

Original Answer (Using --out)

You can do something similar in Visual Studio if you right click on your library project and click properties. In the TypeScript Build tab, check off the option to Combine JavaScript output into file and specify the location in your main project you want it to go (Ex. $(SolutionDir)/TypedApp/External/TypedLibrary.js). Then also check off Generate declaration files in order to generate a .d.ts file.

Setting up project for automated builds

Once this is done, build your library project and then include the .js, and .d.ts in your main project. Include the .js file in your html and reference the .d.ts in your typescript files.

Include files in main project

Each time you rebuild the library project, it will automatically update the main project with the changes.

answered 4 years ago Martyn0627 #3

The solution suggested by @dhsto works but I have found an alternative using linked folders. I have written about it in detail in this article, here is how it can be implemented:

It can be achieved by creating a folder to hold your references, I like to name this “_referencesTS”, the folder will contain all of the links to files from Test1. This can be done individually but would become very cumbersome if it had to be done for each new TS file. Linking a folder however will link all of the files beneath it, this can be done by editing the csproj file.

To edit the file right click the Test2 project and click “Unload Project”, then right click the project and click “Edit Test2.csproj”. Navigate to the <ItemGroup> that contains the <TypeScriptCompile> tags and insert the code below:

<TypeScriptCompile Include="..\Test1\**\*.ts">
   <Link>_referencesTS\%(RecursiveDir)%(FileName)</Link>
</TypeScriptCompile>

Replace the relative path to the location of your TS files in Test1, this uses the wildcarding (*) to link all .ts files (dictated by the *.ts) within all sub folders (dictated by the \**\)..

The TS files within these folders will now appear linked within Test2, allowing for automatic typescript referencing.

Note: The only downside to this approach is that when a new file is added to Test1 within a linked folder, the user has to unload and load the project or close and open the solution for it to appear in Test2.

answered 4 years ago Allan.C #4

I just want to add to the answer of David Sherret that the lib files to the TypedApp project could be added as Link files instead of depending on post build events. I'm having some issues with post build events in big solutions with a lot of projects, and the link files are now working ok for me. (I cannot add a comment to the answer because I only have 35 reputation points).

answered 4 years ago Aebsubis #5

If you need to share the code between multiple projects you can always create a symbolic link on each project where you need it. http://en.wikipedia.org/wiki/Symbolic_link

answered 4 years ago Andre Light #6

basarat's answer is the closest to being the most reliable solution for cross-project TypeScript references. However, when merging shared TypeScript code with a referencing project's TypeScript (important, for example, if you need to target different ECMAScript versions), the Source Map file doesn't resolve to the shared project's directories, so debugging won't work (in fact, Visual Studio often crashes after adding breakpoints to the files referenced in another project).

Linked files of any kind (Visual Studio links and symbolic links) don't work with debugging in any system (Visual Studio, Chrome, WebStorm, etc.)--linked files essentially don't exist to the ASP.NET debugger, nor any other debugger; they exist only in Visual Studio.

Please see this question/answer indicating what has worked great for both solid code maintenance and debugging in Visual Studio, Chrome, and Firefox, while still retaining the ability to combine shared code with the referencing projects' code (important, for example, if you need to target different ECMAScript versions): Visual Studio: How to debug TypeScript in a shared project using IIS Express and cross-project references (no linking or duplicating files)

answered 3 years ago Micah Zoltu #7

Frustrated with the state of affairs, I wrote a NuGet package that mostly solves this problem. Using the NuGet package you can just add a reference from one project to another and it will do the work of copying files around in a way that is safe from accidentally editing the wrong file and still gives intellisense and debugging.

Details can be found in the Readme.md, or you can just install the NuGet package and run (I recommend at least reading the how to use section).

https://github.com/Zoltu/BuildTools.TypeScript.FromReferences

answered 2 years ago Ron Newcomb #8

The accepted answer disallows debugging in Visual Studio when a breakpoint is set in the Shared project. (At best the debugger will stop on a line in the compiled javascript, but not the original Typescript and certainly not in its original project location.)

In the Shared project's properties, let's say that Combine Javascript output into [a single] file is checked and set to AllShared.js, which also makes a AllShared.d.ts file because Generate declaration files is checked, and also makes a AllShared.js.map because Generate source maps is checked.

The referencing project should NOT copy or link these files in the way the accepted solution does. Instead:

Part 1, in the referencing project, create /typings/tsd.d.ts if it doesn't already exist, and append to the bottom of that file the line ///<reference path="../../SharedProject/AllShared.d.ts" />. Once this is done, (and at least one successful compile of SharedProject is done), Intellisense and the Typescript compiler should see Interface1 etc. (You'll likely get a red squiggly line underlining the statement if the path/file doesn't exist, which is nice.)

Part 2, in the referencing project's index.html, add the line <script src="http://localhost:29944/AllShared.js"></script> before that project's own script tags. The localhost part comes from the Shared project's Properties, Web tab, Project Url. (Both 'IIS Express' and 'Local IIS' work.)

Now when you run the referencing project, you should see Internet Explorer** request the relevant files from their respective "websites". Visual Studio breakpoints should be hit regardless whether they're in SharedProject or the referencing project.


. Although this solution works without gulp/grunt/powershell, Visual Studio's Combine Javascript output into one file doesn't glue together the files in any particular order, and it will eventually break your code. Then you'll need to add Gulp/etc. to the referencing project to insert a <script src="http://localhost:29944... tag for each file in Shared***, because keeping index.html updated by hand is a poor option. (Adding Gulp to the Shared project, to concat the .js and .d.ts files into singles runs into an issue with .js.map files, which can't be simply concatted.)

** IE and VS are both Microsoft products, so IE really works better if you want to use VS's breakpoints and debugger instead of a web browser's.

*** Gulp doesn't like injecting urls, only filepaths. Given HTML comments in index.html like <!-- SharedStuff:js --><!-- endinject -->, circumvent this like so:

gulp.task('insert-into-html', [], function () {
    var common = gulp.src(['../SharedProject/**/*.js'], { read: false });
    return gulp.src('./index.html')
            .pipe(inject(common, {
                relative: true,
                name: "SharedStuff",
                transform: function (filepath) {
                    return '<script src="http://localhost:29944/'+filepath+'"></script>';
                }
            }))
            .pipe(gulp.dest('./'));
});

comments powered by Disqus