Creating custom Typescript types for my code in a separate directory

My goal is to develop a library that automatically generates Typescript types for user code. I'd prefer to keep these type definitions in a separate folder for organizational purposes, while still allowing IDEs to recognize them.

While it's feasible with Typescript modules, I've been unable to figure out how to define types for an "ambient" module. Here's a simplified example of the desired file structure:

// src/pages/WelcomePage.ts
export const message = 'hi'
// src/index.ts
import { message } from './pages/WelcomePage'
console.log(message) // message should be of type "Message", not "string".
// types/pages/WelcomePage.d.ts
// This approach does not work.
// Is there a way to make it work?
declare module "pages/WelcomePage" {
    type Message = string
    export const message: Message
}

tldr: I want all my generated types to reside in types/... and define types for src/....

Answer №1

By incorporating path aliases along with the guidance from this answer on Stack Overflow, I was able to successfully get it up and running:

My approach involved utilizing the "transformers" variant from the aforementioned answer, alongside using @ as an alias for the src directory:

// src/pages/WelcomePage.ts
export const message = "aaa";
// src/index.ts
import { message } from "@/pages/WelcomePage"
console.log(message)
// types/pages/WelcomePage.d.ts
declare module "@/pages/WelcomePage" {
    type Message = string
    export const message: Message
}

Additionally, here is the setup for the project: tsconfig.json

{
    "compilerOptions": {
        "module": "CommonJS",
        "moduleResolution": "Node",
        "outDir": "./dist",
        "baseUrl": ".",
        "paths": {
            "@/*": ["src/*"]
        },
        "plugins": [
            {
                "transform": "@zerollup/ts-transform-paths"
                "exclude": ["*"]
            }
        ]
    },
    "include": ["./types/**/*", "./src/**/*"]
}

package.json

{
    "scripts": {
        "build": "ttsc -p ."
    },
    "devDependencies": {
        "@zerollup/ts-transform-paths": "^1.7.18",
        "ttypescript": "^1.5.13",
        "typescript": "~4.4.0"
    }
}

How does this method work? Firstly, the declaration files are included which results in the declaration of the @/pages/WelcomePage module. This declaration is then utilized by TypeScript for verification during import within index.ts.

During the compilation process, the ts-transform-paths is executed and modifies the path @/pages/WelcomePage to the resolved

./pages/WelcomePage</code) ensuring that the final code functions correctly.</p>
<p>However, a downside of this solution is that there is no validation if <code>src/pages/WelcomePage.ts
actually exports what types/pages/WelcomePage.d.ts specifies.

Answer №2

It appears that a custom tsconfig and npm command could potentially solve the issue at hand, assuming I have grasped the question correctly. The tsconfig file can define input/output directories and specify that only declaration files should be generated:

// tsconfig.special.json:

{
    "compilerOptions": {
        "compiler_options": "go_here",
        "emitDeclarationOnly": true, // will only emit .d.ts files
        "outDir": "types"
    },
    "include": ["src/**/*.ts"],
    "exclude": ["node_modules"]
}

This configuration instructs TypeScript to compile all .ts files within the src directory into .d.ts files exclusively, which will be stored in the types directory.

Your npm command within the "scripts" section might resemble the following:

"compile": "tsc --p tsconfig.special.json"

As you mentioned this is intended for a library to be installed by other developers, they will need to create their own npm script to execute this one. Instructions on how to achieve this are available here: How do I run an npm script of a dependent package.

To ensure that TypeScript reads from the correct src folder (i.e., user_project/src instead of

user_project/node_modules/your_library/src
) and outputs to the proper directory (e.g., user_project/types rather than
user_project/node_modules/your_library/types
), adjustments may need to be made to the tsconfig file and npm command. Feel free to ask for further details if needed. Additionally, users implementing your library must include the generated /types directory in their project's tsconfig.json to ensure proper detection of the generated .d.ts files.

I hope this guidance aligns with the help you seek.

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

Tips for transferring errors from effects to actions within NGRX

Having recently delved into NGRX, I am attempting to transfer an error thrown in an effect to an action and store the error object. While this functionality appears to work smoothly on my local setup, it encounters an issue once deployed on the server: Er ...

Exploring Computed Properties in Angular Models

We are currently in the process of developing an application that involves the following models: interface IEmployee{ firstName?: string; lastName?: string; } export class Employee implements IEmployee{ public firstName?: string; public l ...

Attribute specified does not belong to type 'DetailedHTMLProps<ButtonHTMLAttributes

I am working on creating a reusable 'button' component and I would like to include a href attribute so that when the button is clicked, it navigates to another page. An Issue Occurred: The following error was encountered: 'The type '{ ...

Unable to locate the 'typescript' module at <Path>. However, the tsc -v command displays logs

I have a project using nrwl/nx and I set up the workspace(morningharwood) and an app(portfolio) on my home computer (windows). Now, I have cloned the repository, installed the dependencies with yarn install, and attempted to run it on my mac. However, I en ...

Error encountered with default theme styling in Material-UI React TypeScript components

Currently, I am working on integrating Material-UI (v4.4.3) with a React (v16.9.2) TypeScript (v3.6.3) website. Following the example of the AppBar component from https://material-ui.com/components/app-bar/ and the TypeScript Guide https://material-ui.com/ ...

Implementing React with CSS styling is a popular way to provide

I am facing an issue with a reusable component that is supposed to handle two different states based on the use case. The problem arises when trying to apply CSS styles depending on which state is provided. For some reason, it only applies the styles from ...

Encountering errors when examining local variables during unit testing on an Angular component

If we have 2 components, namely AppComponent and TestComponent. The TestComponent is being called using its directive in the HTML template of the AppComponent. Within the TestComponent, there is an @Input() property named myTitle. Unit testing is being pe ...

How to convert typescript path aliases into relative paths for NPM deployment?

I am currently working on a typescript project that utilizes paths for imports. For instance: "paths": { "@example/*": ["./src/*"], } This allows the project to import files directly using statements like: import { foo } from "@example/boo/foo"; Whe ...

How to specify in TypeScript that if one key is present, another key must also be present, without redundantly reproducing the entire structure

In my code, I have a custom type defined like this (but it's not working): type MyType = | { foo: string; } | { foo: string; barPre: string; barPost: string; } | { foo: string; quxPre: string; qu ...

Accessing data from an object of type Request in NodeJS using Typescript

Is there a way for me to retrieve req.kauth.grant It is definitely populated because when I print req, I see this: kauth: { grant: Grant { access_token: [Token], refresh_token: undefined, id_token: undefined, token_type: undefi ...

Steer clear of using Typescript to exclude certain sections of code from compilation

Is there a method to prevent certain code from being compiled by the Typescript compiler based on a variable that needs to be provided in the compiler command? This would be beneficial for developing multi-platform applications where the code is mostly sim ...

Can we destruct and type the properties of a function parameter object that are already known?

Typescript playground Scenario: Creating a function that takes a single object with predefined properties, where we need to destructure and assign simultaneously. The following method works as intended: type OBJECT_PARAM = { pathname: string, routePa ...

Using React-Bootstrap with TypeScript in your project

I'm currently working on creating a navigation bar using react-bootstrap. I've already installed the node-module as follows: "@types/react-bootstrap": "^0.32.11",. However, when I try to use it in my hello.tsx component, I encounter a compile err ...

Is it possible to establish a default or recommend a specific folder arrangement when conducting a direct download without the need for a zip file?

Q: Can a custom folder structure be recommended for direct downloads without using a zip file? My AngularJS application generates text files that need to be imported into a specific folder structure on a commercial machine. Is there a way to prompt users ...

The NestJS framework encountered an error due to a method being undefined in the

Encountering Error with NestJS Function create123: TypeError - Cannot read properties of undefined (reading 'create123') The constructor is displayed below \`export class AuthenticationService { constructor( private readonly usersServ ...

I've encountered an issue where my React website functions correctly on the development server but not on the live website. Where should I start investigating to find the cause of this discrepancy?

I am trying to embed a Datawrapper map using the following code: import InnerHTML from 'dangerously-set-html-content' export function Map1(){ const htmlFile = `<div style="min-height: 374px"> <script type="text ...

The custom native date adapter is facing compatibility issues following the upgrade of Angular/Material from version 5 to 6

In my Angular 5 application, I had implemented a custom date adapter as follows: import {NativeDateAdapter} from "@angular/material"; import {Injectable} from "@angular/core"; @Injectable() export class CustomDateAdapter extends NativeDateAdapter { ...

The error message thrown is: "Unable to assign value to property 'formGroup' as it is not defined

There's a particular component structure as shown below: import { Component, Input } from '@angular/core'; import { WorkingHours } from '../../../../../hours'; import { FormGroup } from '@angular/forms'; @Component({ ...

What causes arrays to unexpectedly change in Angular?

Currently, I am working on a project that involves Angular and Laravel. One interesting issue has arisen in one of my components where I receive a variable from the database as an object array. Surprisingly, even though I only loop through this variable wi ...

Developing a function that determines its return type based on the presence of an optional parameter

Let's explore a JavaScript function and its usage: const fooHandler = function(state, foo) { if (foo) { return {...state, foo} } else { return state.foo } } const currentState = {foo: 'foo', other: 'data'}; fooHandle ...