The Typescript Compiler API Type Checker produces varying types from what is perceived by the Typescript Language support within the VS Code environment

Utilizing the Typescript Compiler API type checker, I am analyzing identifier nodes in a loaded file to determine their types within a program.

To begin, I load the file and initialize the program and type checker like so:

const program = ts.createProgram([document.uri.fsPath], { allowJs: true, strict: true });
const checker = program.getTypeChecker();

const sourceFile = program.getSourceFile(document.uri.fsPath);

Next, I traverse the Abstract Syntax Tree (AST) using ts.forEachChild. Upon finding a node of interest, I use the type checker to retrieve its type as follows:

const type = checker.getTypeAtLocation(node);
const typeAsString = checker.typeToString(type, node, typeFormatFlag);

Consider this sample file:

//spreadArray.tsx

import React from 'react';

interface ComponentProps {
  items: string[];
}

function Component(props: ComponentProps) {
  const {
    items: [item1, item2, ...otherItems]
  } = props;

  return (
    <div className='my-class-2'>
      <div>{item1}</div>
      <div>{item2}</div>
      <div>{otherItems.join(', ')}</div>
    </div>
  );
}

I want to know the types of item1, item2, and otherItems. While hovering over these variables in the original file provides correct types (string, string, string[]), running my program results in incorrect resolutions (any, any, {}). This inconsistency extends to various other types such as arrow functions, promises, and objects.

Running integration tests with files like the above sometimes yields correct types but also exhibits wrong resolutions at times. The codebase and test environment share the same TypeScript version and tsconfig settings.

The project configurations are as follows:

tsconfig.json

{
  "compilerOptions": {
    "jsx": "react",
    "module": "Node16",
    "target": "ES2022",
    "lib": ["ES2022"],
    "sourceMap": true,
    "rootDir": "src",
    "strict": true
  }
}

package.json

//....
"devDependencies": {
    "@types/mocha": "^10.0.6",
    "@types/node": "18.x",
    "@types/react": "^18.2.58",
    "@types/react-dom": "^18.2.19",
    "@types/vscode": "^1.86.0",
    "@typescript-eslint/eslint-plugin": "^6.19.1",
    "@typescript-eslint/parser": "^6.19.1",
    "@vscode/test-cli": "^0.0.4",
    "@vscode/test-electron": "^2.3.9",
    "eslint": "^8.56.0",
    "eslint-config-prettier": "^9.1.0",
    "prettier": "^3.2.5",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "ts-loader": "^9.5.1",
    "webpack": "^5.90.0",
    "webpack-cli": "^5.1.4"
  },
  "dependencies": {
    "typescript": "^5.3.3"
  }

Node version: 18.17.1

In attempting to rectify the issue, I've experimented with alternative methods like getSymbolAtLocation(node) and

getTypeOfSymbolAtLocation(symbol, node)
without success. Various versions of TypeScript 5.x and testing across React and TypeScript projects have resulted in inaccurate type resolutions.

This consistently erroneous resolution, often defaulting to any or unknown and empty objects for arrays, persists across multiple codebases. Notably, more complex expected types tend to exhibit higher chances of incorrect resolution.

I seek insights on what may be causing these discrepancies to improve precision in type determinations, aligning them more closely with IDE variable inspections.

Answer №1

In summary: The typescript program instance was unable to locate the JS API types.


The primary issue: Missing lib argument in compilerOptions

Following Sherret's advice, I examined the diagnostics using the code snippet below:


const program = ts.createProgram([document.uri.fsPath], { allowJs: true, strict: true });

const emitResult = program.emit();
const allDiagnostics = ts.getPreEmitDiagnostics(program).concat(emitResult.diagnostics);

allDiagnostics.forEach((diagnostic) => {
  if (diagnostic.file) {
    const { line, character } = ts.getLineAndCharacterOfPosition(diagnostic.file, diagnostic.start!);
    const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n');
    console.log(`${diagnostic.file.fileName} (${line + 1},${character + 1}): ${message}`);
  } else {      
    console.log(ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n'));
  }
});

Running in debug mode resulted in transpilation and bundling with webpack to the dist folder. Initial error messages in the log indicated issues like Cannot find global type Number for JS API types.

After research, it was discovered that adding the lib option to the compilerOptions should resolve this problem. However, specifying it as suggested by the API caused the program to search for a file named ESNext, leading to failure. Thus, the full file name had to be provided, such as lib.esnext.full.d.ts.

This alone didn't solve the issue, but after further debugging, it was found that only performing transpilation without bundling reduced the emitted errors significantly. Testing in debug mode now produced similar results to test mode.

Note: A notable difference between debug (prior to changes) and test mode was that the latter solely underwent transpilation via tsc to the out folder. Strangely, the program automatically located

@root/node_modules/typescript/lib
without needing to specify lib in compilerOptions when skipping bundling.


Cleaning up other console errors

Upon inspection, additional adjustments were needed to tidy up the console output. Adding jsx and esModuleInterop to the compilerOptions helped improve the program instance within this extension's context. Therefore, the updated program instance appeared as follows:


const program = ts.createProgram([document.uri.fsPath], {
  allowJs: true,
  strict: true,
  lib: ['lib.esnext.full.d.ts'],
  jsx: ts.JsxEmit.React,
  esModuleInterop: true
});


Solving bundling issues

The issue persisted post-bundling, rendering the prior fix ineffective in production. The bundled code continued to report inability to find the lib file at the specified path @root/dist/lib.esnext.full.d.ts. Naturally, it wouldn't exist there, as it resided at

@root/node_modules/typescript/lib
. Directly pointing to the correct path wasn't viable since shipping the node_modules directory was not an option.

Hence, the solution involved copying the lib files to the dist folder during bundling, achieved with the copy-webpack-plugin.

Given that the lib files depended on libraries of previous versions in a cascading manner, those had to be copied too:

// webpack.config.js
const CopyPlugin = require('copy-webpack-plugin');

const extensionConfig = {
  // ...
  plugins: [
    new CopyPlugin({
      patterns: [{ from: 'node_modules/typescript/lib/*.d.ts', to: '[name][ext]' }]
    })
  ]
};

Subsequently, only whitelisting *.d.ts files in .vscodeignore prevented their removal from the production package intended for the VS Code marketplace.

// .vscodeignore
// ...

!dist/*.d.ts

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

Refresh the information being received without initiating a new process

I am fetching data from an ngrx/store I have subscribed to the data this.store.select(somedataList) .subscribe(myList => { myList.propertyA = "a different value"; }); After modifying the property values upon subscription, I must update the data ...

Switching an Enum from one type to another in JavaScript

UPDATE After reading a comment, I realized that Enum is not native to JavaScript but is actually part of TypeScript. I decided to keep the original title unchanged to help others who may also make the same mistake as me. I am faced with a scenario where ...

The formatting in vscode does not apply to .tsx files

For a while now, I've relied on the Prettier extension in Visual Studio Code for formatting my code. However, since switching to writing React with Typescript, I now need to configure Prettier to format .tsx files accordingly. ...

Executing a function on a converted TypeScript object

My experience with using Cloud Firestore has been smooth in casting to an object, but I have encountered an issue when trying to call methods on that object. Below is the model definition I am working with - contact.ts export class Contact { id: string ...

Consolidate type definition within a tsx file using VS Code

Whenever I define a type in a TypeScript file (.ts) on VS Code, I notice that there is no option to collapse the definition. Here's an example: export type Test = { someValue: string, someOtherValue: string, yetAnotherValue: string }; I ...

Display an image in an Angular application using a secure URL

I am trying to return an image using a blob request in Angular and display it in the HTML. I have implemented the following code: <img [src]="ImageUrl"/> This is the TypeScript code I am using: private src$ = new BehaviorSubject(this.Url); data ...

Exploring the most effective strategies for creating a brand-new type in TypeScript?

In the execution environment I'm working with, there are several global constants that represent different directions: TOP = 1 TOP_RIGHT = 2 RIGHT = 3 BOTTOM_RIGHT = 4 BOTTOM = 5 BOTTOM_LEFT = 6 LEFT = 7 TOP_LEFT = 8 These constants are not just ran ...

"The list of table rows in a React application using Typescript is not rendering properly

I am encountering an issue where the rows in my table are not being rendered while trying to map objects from a list called items. I am working with typescript and react-bootstrap. Can someone help me understand why this is happening and how to resolve it? ...

Calculating different percentages from a reduced map containing JSON data

Forgive me in advance if there's a similar question out there, but after a day of searching, I couldn't find what I need. I'm calling an API that responds with data, and I've extracted the necessary details to create a JSON file with t ...

Create a visual representation after inserting

I have created an input feature that allows me to paste images by using CTRL-V. The process involves copying an image from the browser and pasting it into the input field using CTRL-V. The result is an image in base64 format. Is there a way to manipulate ...

Tips on customizing the appearance of the dropdown calendar for the ngx-daterangepicker-material package

Is there a way to customize the top, left, and width styling of the calendar? I'm struggling to find the right approach. I've been working with this date range picker. Despite trying to add classes and styles, I can't seem to update the app ...

A guide to creating a forward reference in React

I have a complex component that I need to type accurately. Here is what I am trying to achieve: interface Selector<V,T> { onChange(value:V): T } export const Selector = forwardRef<V, T>( ( { onChange }: Selector< ...

Is it possible to customize a VSCode extension to function exclusively with certain programming languages?

Lately, I added a new extension to my VSCode setup that has proven to be quite beneficial for my workflow. If you're interested, you can find this helpful extension at This Repository. It allows you to easily highlight the starting and ending syntax ...

Using a For Loop in VueJS with TypeScript for an array of objects

I'm currently working on a for loop within an object array. Below is the code snippet I am working with: private async rightsOverview() { let item: any[] = []; const prod = await fetchFromApi<ProductionBaseType>(`/productions/${id ...

What is the function of the Angular dependency injection mechanism?

I'm trying to grasp the inner workings of Angular/NestJs dependency injection. It's intriguing how the type of parameters gets lost when a Typescript class is constructed. For example: type Dependency1 = {}; type Dependency2 = {}; class X { ...

Identify the signature of a callback function during runtime

My goal is to pass different callback functions as arguments and have them called with the proper parameters. Here is a simplified example of how it should work. However, determining if process instanceof ITwo makes no sense and I haven't found an ex ...

Can you explain the purpose and functionality of the following code in Typescript: `export type Replace<T, R> = Omit<T, keyof R> & R;`

Despite my efforts, I am still struggling to grasp the concept of the Replace type. I have thoroughly reviewed the typescript documentation and gained some insight into what is happening in that line, but it remains elusive to me. ...

Using React for passing data

In the snippet found in "CameraPage.tsx", there is a logical function that is responsible for fetching camera images. This function simply makes a GET request to search for images stored in the backend, which will later be displayed on the FrontEnd. The op ...

How can I create an interceptor in Angular2 to detect 500 and 404 errors in my app.ts file?

Creating an Angular2 Interceptor for Handling 500 and 404 Errors in app.ts In my app.ts file, I am looking to implement an interceptor that can detect a 500 or 404 error so that I can appropriately redirect to my HTML 404 or HTML 500 pages. Is this funct ...

What is the best method for altering a route in React while utilizing Typescript?

I recently started coding along with the ZTM course and am working on a face recognition app. Instead of using JavaScript, I decided to use TypeScript for this project in order to avoid using the any type. However, as a beginner in this language, I'm ...