Is there a way to bring in data from a .d.ts file into a .js file that shares its name?

I am in the process of writing JavaScript code and I want to ensure type safety using TypeScript with JSDoc.

Since it's more convenient to define types in TypeScript, my intention was to place the type definitions in a .d.ts file alongside my .js file:

// person.d.ts
export type Person = {
  name: string;
}
// person.js

/** @type {import("./person").Person} */
let person;

person = {
  name2: "sdf", // <-- this should error, but does not
};

The problem is, this seems to be causing issues with the TypeScript checker.

If I change the name of person.d.ts to foo.d.ts and import ./foo instead, everything works fine.

It's puzzling that TypeScript can see and understand the type, yet fails to interpret it correctly:

https://i.sstatic.net/DWH68.png

Am I missing something here?

UPDATE: Below is my tsconfig.json

{
  "compilerOptions": {
    "target": "esnext",
    "allowJs": true,
    "checkJs": true,
    "rootDir": "."
  },
  "include": ["./**/*.js", "./**/*.ts"]
}

Answer №1

You are currently encountering a rather obscure "feature" within the tsc compiler.

Take a look at this example tsconfig:

{
  "include": [
    "**/*.js",
    "**/*.ts"
  ],
  "compilerOptions": {
    "rootDir": ".",
    "listFiles": true,
    "module": "commonjs",
    "allowJs": true,
    "checkJs": true,
    "noEmit": true,
    "skipLibCheck": true
  }
}
$ tree -L 2
.
├── node_modules
│   ├── reverse-line-reader
│   └── typescript
├── package.json
├── src
│   ├── person.d.ts
│   └── person.js
├── tsconfig.json
└── yarn.lock

Execute tsc:

/home/user/source/module/node_modules/typescript/lib/lib.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.es5.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.dom.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.webworker.importscripts.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.scripthost.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.decorators.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.decorators.legacy.d.ts
/home/user/source/module/src/person.d.ts

Only person.d.ts is recognized by the compiler, despite the existence of person.js. No errors are reported.

If you remove the include section from tsconfig.json:

$ tsc
src/person.js:7:5 - error TS2322: Type '{ name2: string; }' is not assignable to type 'Person'.
  Object literal may only specify known properties, but 'name2' does not exist in type 'Person'. Did you mean to write 'name'?

7     name2: "sdf", // <-- this should error, but does not
      ~~~~~~~~~~~~

/home/user/source/module/node_modules/typescript/lib/lib.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.es5.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.dom.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.webworker.importscripts.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.scripthost.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.decorators.d.ts
/home/user/source/module/node_modules/typescript/lib/lib.decorators.legacy.d.ts
/home/user/source/module/src/person.d.ts
/home/user/source/module/src/person.js

Found 1 error in src/person.js:7

Now, magically, both person.js and person.d.ts are picked up by the compiler. How?

It seems that when utilizing the include directive, tsc tends to exclude files it believes might have been previously generated by the compiler. For instance, running tsc on a person.ts file will produce output files named person.js and person.d.ts (with declaration set to true in tsconfig.json).

The only mention I've come across of this behavior is in this document, although now considered deprecated:

Keep in mind that the compiler excludes files that could potentially be outputs; for instance, if input includes index.ts, then index.d.ts and index.js will be excluded. In general, having files with similar extensions next to each other is discouraged.

Despite being outdated, this behavior still persists today.

In conclusion, one solution is to eliminate the include section entirely. It's unnecessary in simpler cases, such as this one. Alternatively, as suggested in the documentation, relocate your types to a separate folder or consolidate them into a single file.

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

Getting the value of a JavaScript variable and storing it in a Python variable within a Python-CGI script

Is there a way to capture the value of a JavaScript variable and store it in a Python variable? I have a Python-CGI script that generates a selection box where the user can choose an option from a list. I want to then take this selected value and save it ...

Error: Unable to access the 'create' property of an undefined object while utilizing sequelize to register a user and add an entry

Whenever I try to execute this controller, an issue arises indicating a problem with the recognition of the .create method from the model. What is the correct way to import Sequelize in order to utilize it effectively? const db = require("../Models/m_use ...

The unique Angular type cannot be assigned to the NgIterable type

As a newcomer to Angular, I was introduced to types and interfaces today. Excited to apply my new knowledge, I decided to enhance my code by utilizing a custom interface instead of a direct type declaration: @Input() imageWidgets: ImageWidget; Here is the ...

Navigating with Angular 6 using routerlink in a child module with router-outlet specified in the parent component (app.component

I'm currently working with the RouterModule and encountering an issue with the routerLinks. The problem I am facing is that the routerLinks are not functioning properly (the anchor tags are not clickable). This issue arises because they are located w ...

Replace jQuery CSS with standard CSS without using !important to override styles

Recently, I encountered a puzzling situation: I have an element with the following CSS properties ($(this) represents the cloned object): clone.css({ 'width': $(this).width() + 'px', 'height': $(this).height() + ' ...

What steps should I take to verify the validity of an Angular form?

I am struggling with validating an inscription form in HTML. Despite trying to implement validations, the inputs are still being saved in the database even when they are empty. Here are the validations I want to include: All inputs are required Email addr ...

Show categories that consist solely of images

I created a photo gallery with different categories. My goal is to only show the categories that have photos in them. Within my three categories - "new", "old", and "try" - only new and old actually contain images. The issue I'm facing is that all t ...

Error: Module 'react' not found. Please make sure it is installed and correctly imported

Recently, I've been working on developing a React app using TypeScript. To kickstart the project, I used yarn create react-app (name) --use-pnp --typescript. However, I encountered an issue with the TS linter repeatedly showing the error: Cannot find ...

Guide on incorporating Kendo UI typings into a TypeScript module

Currently, I am working with Kendo UI for React and TypeScript. My goal is to import the Kendo UI typings for TypeScript using a "typeof import". Following the guidance provided at https://docs.telerik.com/kendo-ui/third-party/typescript, I successfully i ...

Creating a dynamic visual experience with Angular 2: How to implement multiple font colors

I have a text area which is connected to one string, with the default text color set to white. <textarea style="background-color: black;color:#fff;" [(ngModel)]="outputText"></textarea> The connected string contains multiple variables. retur ...

Finding the maximum value among multiple variables in AngularJS

After performing calculations, I have assigned specific values to variables. console.log("Gain_weight is "+ Gain_weight); console.log("Gain_smoking is "+ Gain_smoking); console.log("Gain_exercising is "+ Gain_exercising); console.log("Gain_foodhabits ...

Attempting to save MongoDB data into a variable for integration with Handlebars.js

Having an issue with storing MongoDB data in a variable to display in HTML using hbs. The specific error message is TypeError: Cannot read property 'collection' of undefined. Here's the code snippet I have written: const express = require(& ...

In an Electron-React-Typescript-Webpack application, it is important to note that the target is not a DOM

Rendering seems to be working fine for the mainWindow. webpack.config.js : var renderer_config = { mode: isEnvProduction ? 'production' : 'development', entry: { app: './src/app/index.tsx', //app_A: './src/a ...

Exporting methods/functions in React Native

import { width as responsiveHeight } from "react-native-responsive-dimensions"; I am trying to export responsiveHeight with the name width. Can someone please guide me on the correct way to do this? The current approach is not yielding any results. ...

There is a missing dependency in the root installation of a custom node module

Currently, I have a project for an app and two separate node module projects. The dependency structure looks something like this: App { NodeModule1 { NodeModule2, ... }, ... } The issue I am facing is that instead of NodeModule2 being instal ...

What is the reason for the getter not being able to retrieve the value

I am experiencing an issue with a getter that returns an array of objects. The challenge I face is that I need to display past and current warnings in separate components. The getter currently only retrieves the current warning and ignores any past warnin ...

Filling an HTML template with an $http response in VueJS

After learning about VueJs, I decided to embark on a small project - a nutrition app where food recommendations are made based on specific nutrients. Below is the JavaScript code snippet: recommendFood: function() { this.recs = {}; ...

SignalR's postback interrupts the functionality of jQuery actions

On my screen, I have a widget that updates data and changes its class based on server-side interactions. It also responds to mouse clicks. To notify multiple clients of updates simultaneously, I'm using SignalR. The problem arises when I wrap everythi ...

Instructions for capturing multi-dimensional arrays using forms in Vue

I have a snippet of code that looks like this: <div class="form-item"> <label for="list-0"><?php _e('List 0', 'test'); ?></label> <input name="list[0]" type="text" id="list-0" value=""> </div> &l ...

The UI-Grid feature in Angular, when set to right-to-left (RTL) mode, incorrectly displays data in the opposite order compared to the

In my _Layout.cshtml file, I have a CSS that sets all UI controls to right-to-left direction using the following code: * { direction: rtl; } Currently, I am working with Angular's UI-Grid, and in RTL Localization, the ...