Using TypeScript absolute imports from another project for standard TypeScript files and declarations at a global scope

We are currently considering migrating a large JavaEE code base to TypeScript. Within this environment, there are multiple projects with intricate directory structures and JavaScript code that heavily relies on global variables across all projects.

Although TypeScript does offer multi-project support, as showcased in this small example on GitHub, our situation is more complex.

(1) The JS code is deeply embedded within the projects, for instance "Project1/src/main/resources/de/mycompany/webapp/ts". The resulting JS code should then be located at "Project1/src/main/resources/de/mycompany/webapp/ts-generated". TypeScript provides options like rootDir and outDir to handle this scenario.

(2) Due to the intricate folder structure, using relative imports is not feasible. For example, referencing a file from the 'core' project should have a consistent approach, regardless of whether it is accessed from the 'ui' or 'ui-utils' project. TypeScript introduces "baseUrl" and "paths" for absolute references, yet implementation seems challenging in a multi-project setup (or perhaps an error on our end). An example would be wanting to

import * from '@core/utils/mycode.ts'
, where this reference should align with the 'core' project's baseUrl or rootDir.

(3) There is a need to make connections to existing JS code in the global scope. TypeScript addresses this by providing declaration files (.d.ts) which can enhance the compiler's understanding through additional type information. Therefore, our plan involves generating declaration files for the current JS codebase, following by importing these declarations into consuming TypeScript projects. Nevertheless, we've encountered challenges where the import-keyword in TypeScript consistently imports declarations as a module with a namespace, despite the intention to link the JS objects to the global scope. We came across the concept of Triple-Slash import, which appears effective for relative references but lacks support for absolute references spanning different projects.

Questions

Regarding point (2): Is there a way to enable absolute imports across projects, preferably starting from baseUrl or rootDir?

Concerning point (3): Can TypeScript declarations from other projects be imported (or referenced) for JS code present in the global scope? It's crucial to note that the goal here isn't to generate code, but solely to inform the compiler about the existence of specific code.

Answer №1

Concerning (3)

In order to incorporate global scope type declarations (.d.ts files) in TypeScript, you can utilize the "typeRoots" array in tsconfig.json: "typeRoots" : ["./typings"]. It is important to note that these files must also be accessible from paths specified in the "include" array ("include": ["./typings/*.ts"]).

This functionality is made possible because the paths defined in "typeRoots" and "include" can reference directories outside the current project.

Concerning (2)

It appears that achieving this with a single call to the TypeScript compiler may not be feasible. However, it can be accomplished by compiling projects sequentially based on their dependencies.

For instance, assuming that a project P1, which has no dependencies, has already been compiled. The objective now is to compile the project P2, which relies on code from P1. In the given scenario, the "outDir" of P1 is designated as "build":

  • Include all ts files from the "outDir" of P1 using the "includes" array of P2.
    • "include": ["../P1/build/*.ts"]
    • This ensures that the compiled d.ts files from P1 are accessible in P2, enabling the TypeScript compiler to recognize the code from P1.
  • Add a path alias to the "outDir" of P1 via the "paths" array of P2.
    • "paths": { "@P1/*": ["../P1/build/*"] }
    • This enables absolute imports to function correctly.

This approach works due to the fact that the paths specified in the "paths" and "includes" arrays can refer to directories beyond the current project.

It should be noted that this method does not leverage the Project References feature of TypeScript. Additionally, unless there is a requirement to provide type information for plain JS code, typeRoots are unnecessary as the type information should already exist in the "outDir" of P1.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

The module declaration is triggering a lint error that reads "Unexpected token, expecting '{'"

To address the absence of available types for a library, I created the file omnivore.d.ts in TypeScript. Within this file, I added the following line: declare module '@mapbox/leaflet-omnivore' Upon running vue-cli-service lint in my project, an ...

The process of invoking another component's method in Angular 2

Is there a way to call a function from one component in Angular 2 into another? I have two components and I need to invoke a method defined in the other component. The components do not have a parent-child relationship, as the overview component is a rout ...

What is the best way to resolve the "unknown" type using AxiosError?

I'm currently working on developing a customized hook for axios, but I've encountered the following error: Argument of type 'unknown' is not assignable to parameter of type 'SetStateAction<AxiosError<unknown, any> | unde ...

Validating nested objects in YUP with the potential for zero or multiple properties present

I am currently working on setting up yup validation for this object: placements: { 3: {}, 5: {}, 6: {0: 'D17'}, 7: {}, 8: {}, 9: {}, 10: {}, 11: {}, } The challenge I am facing is that an entry like 3: {} can be empty, and that's totally fi ...

Testing the React context value with React testing library- accessing the context value before the render() function is executed

In my code, there is a ModalProvider that contains an internal state managed by useState to control the visibility of the modal. I'm facing a dilemma as I prefer not to pass a value directly into the provider. While the functionality works as expecte ...

How can we efficiently load paginated data from a database while still implementing pagination using Angular Material?

I have a large table with more than 1000 entries that I want to display using a <mat-table></mat-table>. Since loading all the entries at once would be too much, I am looking to implement pagination and load only 20 entries per page. The chal ...

What could be causing NgModel to fail with mat-checkbox and radio buttons in Angular?

I am working with an array of booleans representing week days to determine which day is selected: selectedWeekDays: boolean[] = [true,true,true,true,true,true]; In my HTML file: <section> <h4>Choose your days:</h4> <mat-che ...

Jest is unable to handle ESM local imports during resolution

I am encountering an issue with my Typescript project that contains two files, a.ts and b.ts. In a.ts, I have imported b.ts using the following syntax: import * from "./b.js" While this setup works smoothly with Typescript, Jest (using ts-jest) ...

Issue with ng2-charts not rendering properly on the client side when utilized in Angular version 2.0.0-beta-17

Struggling with using ng2-charts in my Angular 2 app and encountering some challenges. app.ts import {Component} from 'angular2/core'; import {CHART_DIRECTIVES} from 'ng2-charts/ng2-charts'; @Component({ selector: & ...

TS - deduce the specific type of a key value without receiving a union type

Welcome to the coding playground: Click here to start coding Let's talk about a scenario where a function is expected to return some value based on an input argument. The challenge arises when there are keys with the same name but different types re ...

Accessing environment-based constants in TypeScript beyond the scope of Cypress.env()Is there a way to gain access to environment-specific constants

Imagine I have an API test and the URL and Credentials are different between production and development environments: before("Authenticate with auth token", async () => { await spec().post(`${baseUrl}/auth`) .withBody( { ...

Change icons in Ionic 5 when selecting a tab

How can I change my tab icons to outline when not selected and filled when selected? The Ionic 5 Tabs documentation mentions a getSelected() method, but lacks examples on its usage. I plan to utilize the ionTabsDidChange event to detect tab clicks, then ...

Where can I locate htmlWebpackPlugin.options.title in a Vue CLI 3 project or how can I configure it?

After creating my webpage using vue cli 3, I decided to add a title. Upon examining the public/index.html file, I discovered the code snippet <title><%= htmlWebpackPlugin.options.title %></title>. Can you guide me on how to change and cu ...

Using p5.js with TypeScript and Webpack is not supported

I'm currently working on a library project that involves utilizing p5.js. Specifications Here is a snippet of my Webpack configuration: const path = require('path'); module.exports = { entry: './start.ts', output: { ...

Is there a workaround for utilizing reducer dispatch outside of a React component without relying on the store?

Recently, I implemented a reducer in my project that involves using react, typescript and nextJS. I am wondering if there is a method to trigger the reducer outside of a react component, such as from an API service. While searching for solutions, most re ...

Trying out the results of angular services

Seeking assistance in understanding the current situation: I believe a simple tour of heroes app would be helpful in clarifying, I am looking to set up some tests using Jest to verify if the behavior of a service remains consistent over time. This is ho ...

Tips for implementing react-hook-form in an Ionic Modal?

I've been experimenting with implementing react-hook-form in my Ionic React project. Here's a simple form I created: const CustomForm: React.FC<{ color: string }> = ({ color }) => { const { handleSubmit, register } = useForm(); con ...

Uh-oh! A circular dependency has been detected in the Dependency Injection for UserService. Let's untangle this web and fix the issue!

Encountering the following error: "ERROR Error: Uncaught (in promise): Error: NG0200: Circular dependency in DI detected for UserService." The auth.component.ts utilizes the UserService and User classes, while the user.service.ts only uses the User class. ...

Choose the AuthGuard category in real-time

My application intends to employ two distinct authentication strategies - one for users accessing via a browser and another for the public API. A specific header will be set for browser users, allowing my app to determine the appropriate auth strategy base ...

Arrow functions do not function properly with Typescript decorators

I've created a typescript decorator factory that logs the total time taken to execute a function, along with the actual function execution results and parameters passed to the decorator. For example: export function performanceLog(...args: any[]) { ...