Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Disable type checking for node_modules entirely #40426

Open
5 tasks done
gempir opened this issue Sep 8, 2020 · 55 comments
Open
5 tasks done

Disable type checking for node_modules entirely #40426

gempir opened this issue Sep 8, 2020 · 55 comments
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript

Comments

@gempir
Copy link

gempir commented Sep 8, 2020

Search Terms

skipLibCheck
node_modules
ignore library
exclude

Suggestion

Either a new option or the existing option skipLibCheck should be able to disable type checking for node_modules.

Use Cases

A library author might have a faulty import or might only provide typescript files for his library and not transpiled code. As a user of this library I want to be able to disable type checking for a library (or all) because I trust this library has been tested enough in other ways.

Another issue where I found a library shipping ts files and causing errors for the user: #15363

The response at the end is very relevant

I understand that libraries shouldn't publish *.ts files, but sometimes, they just do, and we can't control every other library easily.
I think typescript should not try to apply this kind of setting (here noImplicitAny) to the node_modules simply because it is meant to be applied to the user code being compiled, not the external dependencies.

Originally posted by @victornoel in #15363 (comment)

Examples

I had the case that a library came with a dist/ folder and almost all types were perfectly arranged in .d.ts files. But one file was making an import from ../index.d.ts(which seems to be for development purposes, the lib main file is dist/index.d.ts) which in turn then imported src/ClassA.ts

The library author used a different tsconfig and didn't use strict type checking, but I do and that's why the type check fails for me, because now the typescript source files of the library author are type checked.

Checklist

My suggestion meets these guidelines (in case of a new option):

  • This wouldn't be a breaking change in existing TypeScript/JavaScript code
  • This wouldn't change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn't a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.
@DanielRosenwasser
Copy link
Member

As a user of this library I want to be able to disable type checking for a library (or all) because I trust this library has been tested enough in other ways.

Okay, this is one of the reasons you might use skipLibCheck.

I had the case that a library came with a dist/ folder and almost all types were perfectly arranged in .d.ts files. But one file was making an import from ../index.d.ts(which seems to be for development purposes, the lib main file is dist/index.d.ts) which in turn then imported src/ClassA.ts

This is the opposite of the scenario you're describing. The library author has shipped incoherent types, and who's to say what the correct interpretation of that is? It is good that TypeScript errors here, even if it is an overall terrible user experience.


"Disabling type-checking" can mean a lot of things. Having the package come in as any is one possibility. If that's what you're looking for, then you can add a single global file with declare module "incorrectly-authored-library"; until the package ships correct types. Otherwise, I'm not sure what you might have in mind.

@DanielRosenwasser
Copy link
Member

DanielRosenwasser commented Sep 9, 2020

I want to mention after reading this a second time: shipping .ts files is generally nice when using --declarationMaps but I don't know how to best address the issue of different settings between your project and the library's project. Figuring out good prescriptive advice for doing that might be one strategy here.

@gempir
Copy link
Author

gempir commented Sep 9, 2020

Yeah there are lots of good reasons to actually have the sources of a library available, I like to explore them in my IDE sometimes or maybe while debugging etc.

But I don't actually want to have them checked by the typescript compiler, hence the skipLibCheck. It's just so confusing to me why the skipLibCheck is only intended for typing files and not for .ts files

@RyanCavanaugh RyanCavanaugh added Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript labels Sep 9, 2020
@RyanCavanaugh
Copy link
Member

This scenario is going to be completely busted for a lot of reasons. It seems like the option one would really want, if they found themselves stuck in it, would be to say that .ts files under some given paths should be treated as if they were .d.ts files (don't emit, don't typecheck under SLC, don't use to compute rootDir, etc) but have their implementations used for inference. That said, this is just critically a misconfiguration and it's possibly counterproductive to do gymnastics to make it work.

@gempir
Copy link
Author

gempir commented Sep 10, 2020

I think .ts files imported by .d.ts files should be treated the same way, so if skipLibCheck is enabled it should ignore them.

If you are importing .ts files from actual source files like dist/main.js importing ../src/ClassB.ts then i just expect it to break. That shouldn't be ignored.

@shadow-light
Copy link

Could there be a way to just silence errors from certain paths?

I don't want to make a whole import from node_modules type any as 99% of the types are fine and are useful for code completion, but I don't want to hear about the 1% of types that don't adhere to my tsconfig when I can't do anything about them (aside from fork the whole module for the sake of a couple of errors).

Noting that a single type check fail would break CI unless I silence it some how.

@shadow-light
Copy link

(I tried @ts-ignore above the import with the issue, but the errors are with certain files in that imported module, so obviously didn't work)

@NonkelDaniel
Copy link

I am having a very similar issue with a node_module that is included locally in package.json with a path: file:.... there are type errors happening inside of that library and the compiler does not ignore them despite having "skipLibCheck": true in the config.

@shadow-light
Copy link

I had the case that a library came with a dist/ folder and almost all types were perfectly arranged in .d.ts files. But one file was making an import from ../index.d.ts(which seems to be for development purposes, the lib main file is dist/index.d.ts) which in turn then imported src/ClassA.ts

The library author used a different tsconfig and didn't use strict type checking, but I do and that's why the type check fails for me, because now the typescript source files of the library author are type checked.

Just ran into this exact scenario trying to type check my build scripts. Seems like not many people do that so some build-related modules have issues that go unnoticed.

shadow-light added a commit to shadow-light/vite-plugin-vue2 that referenced this issue Aug 20, 2021
Importing from typescript source files distributed with a module causes user projects to type check those source files using the tsconfig of the user project rather than that of the module, with stricter settings raising errors that would otherwise go unnoticed.

See microsoft/TypeScript#40426
@shadow-light
Copy link

shadow-light commented Aug 20, 2021

Here's an example of a project that seems to be doing something benign, simply importing an interface for the sake of type checking. But the import path points to distributed typescript source files rather than the compiled JS. And while there aren't any issues with the types of that module, because my project has stricter type check settings it causes issues.

https://github.com/underfin/vite-plugin-vue2/pull/125/files

underfin pushed a commit to underfin/vite-plugin-vue2 that referenced this issue Aug 21, 2021
Importing from typescript source files distributed with a module causes user projects to type check those source files using the tsconfig of the user project rather than that of the module, with stricter settings raising errors that would otherwise go unnoticed.

See microsoft/TypeScript#40426
antfu pushed a commit to vitejs/vite that referenced this issue Oct 29, 2021
Importing from typescript source files distributed with a module causes user projects to type check those source files using the tsconfig of the user project rather than that of the module, with stricter settings raising errors that would otherwise go unnoticed.

See microsoft/TypeScript#40426
@nitram-work
Copy link

nitram-work commented Nov 23, 2021

I'm having this problem with the lib tst-reflect/tst-reflect-transformer: "TS6133 declared but value never read"
I want that strict checking on our project but would love if it was not enforced on specific libraries I hand pick.

Compiler opts, ran with ttypescript's ttsc:

  "compilerOptions": {
    "allowSyntheticDefaultImports": true,
    "alwaysStrict": true,
    "esModuleInterop": true,
    "module": "commonjs",
    "noEmitOnError": true,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "sourceMap": true,
    "strict": true,
    "strictFunctionTypes": false,
    "useUnknownInCatchVariables": false,
    "noImplicitThis": false,
    "target": "ES6",
    "lib": [
      "es6",
      "dom",
      "es2015.promise",
      "es2016.array.include"
    ],
    "outDir": "build",
    "plugins": [
      { "transform": "tst-reflect-transformer" }
    ]
  },

@icywolfy
Copy link

icywolfy commented Dec 8, 2021

We have been forced to disable strict: true on all our repos.
Third-party libraries ship only .ts files. The package.json has entry point as index.ts. After a year of requests and pressure, no progress has been made convincing other companies to make changes to what they publish.

The code compiles properly, and works at runtime. However, we wish to have strict: true enabled in our builds. However this is not possible. Required packages publish only raw .ts files, which do not meet strict: true, preventing automated enforcement of standards within code that we do have control over.

Being unable to enable any strict typing, solely because we consume third-party packages that don't meet the same strictness, and of which we have no leverage for change:

  • use of any,
  • function type mismatches (../../node_modules/@***/***/src/sdk/*****Api.ts:302:9 - error TS2322: Type '(value?: void | PromiseLike<void>) => void' is not assignable to type '(value?: unknown) => void'
  • ../../node_modules/@***/***/src/sdk/*****Api.ts:21:16 - error TS2532: Object is possibly 'undefined'

We have a manual process that is sometimes applied:

  • check out a PR,
  • manually perform a tsc --noEmit --project tsconfig-strict.json,
  • filter out hundreds of errors from node_modules/ .ts file only libraries
  • find errors in code we own
    Which is error prone, and allows typing problems in our code to slip through.

I had suggested something similar to this : #44205. But it was rejected as "configuration issue".

@aprilmintacpineda
Copy link

aprilmintacpineda commented Jan 18, 2022

Can we please get a flag to suppress any error messages from files originating from node_modules? Doesn't matter if there are 1 million errors if they are in node_modules, just ignore them, don't display them, and exit with code 0. The only time when we should exit with code 1 is when there are errors anywhere else other than node_modules

skipLibCheck is NOT a solution, it's going to disabled ALL declaration files checks, including YOURS!

An example of unwanted error:

node_modules/react-native-ui-lib/typings/generatedTypes/colors.d.ts:38:5 - error TS2300: Duplicate identifier 'grey10'.

38     grey10: string;
       ~~~~~~

node_modules/react-native-ui-lib/typings/generatedTypes/colors.d.ts:39:5 - error TS2300: Duplicate identifier 'grey20'.

39     grey20: string;
       ~~~~~~

node_modules/react-native-ui-lib/typings/generatedTypes/colors.d.ts:40:5 - error TS2300: Duplicate identifier 'grey30'.

40     grey30: string;
       ~~~~~~

node_modules/react-native-ui-lib/typings/generatedTypes/colors.d.ts:41:5 - error TS2300: Duplicate identifier 'grey40'.

41     grey40: string;
       ~~~~~~

node_modules/react-native-ui-lib/typings/generatedTypes/colors.d.ts:42:5 - error TS2300: Duplicate identifier 'grey50'.

42     grey50: string;
       ~~~~~~

node_modules/react-native-ui-lib/typings/generatedTypes/colors.d.ts:43:5 - error TS2300: Duplicate identifier 'grey60'.

43     grey60: string;
       ~~~~~~

node_modules/react-native-ui-lib/typings/generatedTypes/colors.d.ts:44:5 - error TS2300: Duplicate identifier 'grey70'.

44     grey70: string;
       ~~~~~~

node_modules/react-native-ui-lib/typings/generatedTypes/colors.d.ts:45:5 - error TS2300: Duplicate identifier 'grey80'.

45     grey80: string;
       ~~~~~~

node_modules/react-native-ui-lib/typings/generatedTypes/colors.d.ts:46:5 - error TS2300: Duplicate identifier 'grey10'.

46     grey10: string;
       ~~~~~~

node_modules/react-native-ui-lib/typings/generatedTypes/colors.d.ts:47:5 - error TS2300: Duplicate identifier 'grey20'.

47     grey20: string;
       ~~~~~~

node_modules/react-native-ui-lib/typings/generatedTypes/colors.d.ts:48:5 - error TS2300: Duplicate identifier 'grey30'.

48     grey30: string;
       ~~~~~~

node_modules/react-native-ui-lib/typings/generatedTypes/colors.d.ts:49:5 - error TS2300: Duplicate identifier 'grey40'.

49     grey40: string;
       ~~~~~~

node_modules/react-native-ui-lib/typings/generatedTypes/colors.d.ts:50:5 - error TS2300: Duplicate identifier 'grey50'.

50     grey50: string;
       ~~~~~~

node_modules/react-native-ui-lib/typings/generatedTypes/colors.d.ts:51:5 - error TS2300: Duplicate identifier 'grey60'.

51     grey60: string;
       ~~~~~~

node_modules/react-native-ui-lib/typings/generatedTypes/colors.d.ts:52:5 - error TS2300: Duplicate identifier 'grey70'.

52     grey70: string;
       ~~~~~~

node_modules/react-native-ui-lib/typings/generatedTypes/colors.d.ts:53:5 - error TS2300: Duplicate identifier 'grey80'.

53     grey80: string;
       ~~~~~~

Okay, so? I don't really mind that there are type errors on the libraries we use, I only mind that there are type errors on the codes that we wrote anywhere else other than node_modules.

I have 121 errors all of which are from node_modules.

Can someone explain what's the reasoning why we would want to be blocked by errors coming from node_modules? I want to implement a tsc --noEmit on a github check so that every PR will be checked before we event want to review the changes. When the github check fails, we should not be able to merge the PR because the check ensures code quality.

@jespertheend
Copy link
Contributor

My project is fully in js but I do use IntelliSense with strict typing so that I get early warnings when mistakes have been made. But since both checkJs and strict are enabled, I now get a ton of errors from node_modules.

Normally this wouldn't be a huge issue, but I have also enabled Project Diagnostics, which makes errors from all files show up, even if they're not open. Which makes it hard to see a warning whenever a file contains a mistake. If only a single file has an error, it immediately sticks out like a sore thumb, whereas if you permanently have a bunch of files and folders that are red, not so much. Not only that, the Problems view now has a huge list of errors that makes it impossible to find errors caused by me, unless I filter them first.

I also wish to include type checking in CI, but this makes that impossible without writing some sort of plugin to filter all unnecessary errors.

As far as I'm aware, there's a few options to fix this:

  • Disable checkJs and place @ts-check at the top of all my files. This is prone to mistakes, but a linter rule might be able to prevent that.
  • Add @ts-nocheck for all files with errors in node_modules. But it would have to be done with some sort of script to prevent overwrites when updating modules.
  • Disable Project Diagnostics, but this means I won't get warnings from files that are not open. And making a change to a file doesn't necessarily mean new errors will occur in that file only.

But neither of these seem like a satisfying solution to me. If it were possible to add a list of paths to the tsconfig/jsconfig similar to the include and exclude properties, it would be possible to just ignore "node_modules/**/*.js" or "node_modules/*" entirely. This would basically function the same as placing @ts-nocheck at the top of each file. This seems like a nice solution to me, with the added benefit that files other than those in the node_modules directory can be configured to be ignored as well.

@snowinmars
Copy link

The irony is that this issue is not about no-name packages.

Microsoft authentication library for js does throw an error during build under strict ts mode: Type 'string | undefined' is not assignable to type 'string'

Btw, check out their average code quality) Declared return type - 'Account', actual return type - 'Account | null'.

@RyanCavanaugh
Copy link
Member

@snowinmars That library has a correct typings field so you shouldn't be seeing that error, can you clarify how your project is configured? https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-core/package.json#L24

An external library not having strictNullChecks on is not anyone's defect

@snowinmars
Copy link

@snowinmars That library has a correct typings field so you shouldn't be seeing that error, can you clarify how your project is configured? https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-core/package.json#L24

An external library not having strictNullChecks on is not anyone's defect

Msal can be imported in two ways:

  • you can import prebuild items from lib-* folders - and that will not break your build
  • you can import sources from src folder itself to build it by yourself - and that will release the chaos

It's about import { AuthError } from 'msal' vs import { AuthError } from 'msal/src/error/AuthError'

Technically, one can say, that that's what prebuilds are for. But I would not agree: if developers doesn't want the source to be linked, they do not include it to npm package. So, it's a legal way to use a library, that just doesn't match with another's developer way.

If typescript compiler could solve this kind of issues, that'd be cool.

P.S. There is a @azure/msal-browser package with MUST better typings.

@RyanCavanaugh
Copy link
Member

If the MSAL authors are telling you to import the TS sources directly from their packages, please tell me where so I can go tell them to fix their docs. This isn't a supported scenario and there's no end of problems this will cause.

@RyanCavanaugh
Copy link
Member

Maybe I'm missing understanding something, but isn't this mixing two separate things?

OP was specifically referring to importing .ts files:

A library author might have a faulty import or might only provide typescript files for his library and not transpiled code. As a user of this library I want to be able to disable type checking for a library (or all) because I trust this library has been tested enough in other ways.

and

I understand that libraries shouldn't publish *.ts files, but sometimes, they just do, and we can't control every other library easily.

@aspiers
Copy link

aspiers commented Jan 10, 2023

That still leaves the question of why it's not OK to be able to silence errors relating to *.d.ts files in selected paths

This is effectively already what skipLibCheck does, though

I already tried that and in several cases it didn't help. Your use of the word "effectively" seems to acknowledge that there are caveats, and so does your comment on #47387.

basically the only errors you'll see under this setting are the ones that are true problems that TS encountered when evaluating the relevant portions of the .d.ts for your own code.

I think maybe we have slightly different ideas of what constitutes "true problems". You seem to be viewing it from a more purist perspective of whether there are genuine compilation issues within the codebase. This is of course perfectly valid and quite understandable given the nature of your work on TypeScript!

In contrast however, I am more concerned with being able to implement needed functionality in my codebase whilst having its CI and linting set up for a pragmatic balance between theoretical correctness and being able to get my job done. That means that if I encounter some errors which I know are not serious issues in my context (for example if they relate to library code which I'm not even using), then as an experienced software engineer I want to be able to make a judgement call on the risk, temporarily remove those obstructions by downgrading them to warnings, and then return to them later as lower priority concerns.

I suspect many of the people following this issue and similar ones like #44205, #30511, #47387 are closer to my position than yours, otherwise we wouldn't be seeing so many 👍 and ❤️ emojis on the OP.

Maybe I'm missing understanding something, but isn't this mixing two separate things?

OP was specifically referring to importing .ts files:

A library author might have a faulty import or might only provide typescript files for his library and not transpiled code. As a user of this library I want to be able to disable type checking for a library (or all) because I trust this library has been tested enough in other ways.

Specifically but not exclusively. Notice the repeated use of the word "might".

A library author might [...] or might [...]

Two examples were given, the first of which had nothing to do with libraries providing typescript source files. And this was under a "Use cases" header, after the original issue title and "Suggestion" header, neither of which mentioned anything about libraries providing them.

But regardless of the OP's intentions, my point is that there definitely two orthogonal topics here, and we need to consider the ability to selectively silence or downgrade errors from selected paths, regardless of whether those errors are being caused by *.ts files in third party libraries. If you prefer, I'm happy to submit a new issue to keep them separate, but I don't think the title and topic of conversation would change significantly.

@MicahZoltu
Copy link
Contributor

MicahZoltu commented Jan 15, 2023

Please redirect me if this isn't the right thread to bring this up; searching the issues on this topic is hard because ts, d.ts, declaration show up in almost every issue in this repository.

I have a dependency with an index.js, index.d.ts, index.d.ts.map, and index.ts all in the root folder. package.json for the dependency looks something ilke:

{
  "type": "module",
  "main": "index.js",
  "module": "index.js",
  "files": [
    "index.js",
    "index.d.ts",
    "index.d.ts.map",
    "index.ts"
  ]
}

For reasons I cannot understand, when I import it as a named package (e.g., import { ... } from 'package') TSC finds index.ts and tries to use that instead of using index.d.ts.

I can work around it temporarily by manually editing the dependency's package.json to contain "types": "index.d.ts", but it really feels like that should be the default, not index.ts.

Is this something already being talked about above? It seems like if TSC just defaulted to .d.ts when there is no types: field in package.json then some subset of problems would go away. Why is it preferring .ts files for named packages?

At the least, it seems like the docs should be corrected if this is intended behavior (docs say that .d.ts will be used by default): https://www.typescriptlang.org/docs/handbook/declaration-files/publishing.html#including-declarations-in-your-npm-package

@avidj
Copy link

avidj commented Jan 23, 2023

There seem to be loads of libraries causing this issue when a project or its dependencies depend on them. In this case, I understood, there is not much you can do about it except being less strict with your own code. This, unfortunately, will lead to issues sneaking into your own code. It would be nice to be able to enforce strict type checking on your own code to keep the quality high, but to accept the problems in dependencies that you have no realistic possiblity to fix.
Therefore, IMO there should be an option to turn these errors into warnings. After all, this is the purpose of warnings, notifying about potential problems that can lead to failures in the future.
This could be done for all node_modules/per dependency/per folder/per file to allow for maximum flexibility.

@JacobDel
Copy link

@avidj the only option I see is to fork the library and generate the *.d.ts files yourself.
Or at least that's what I'm trying to do in my case

@erikeckhardt
Copy link

erikeckhardt commented Jan 30, 2023

I turned on skipLibCheck: false recently precisely because I absolutely value type checking on my hand-written .d.ts files. These do not duplicate anything in .ts files. They contain things like ambient interfaces that are implemented in concrete classes elsewhere in .ts files. Or special types that are used throughout my project.

First, I had several errors in my .d.ts files that I wanted to know about. This was great! I regretted that checking .d.ts files is not turned on in TypeScript by default. This seems weird. "Why wouldn't developers want that?!?!" I thought.

But then I discovered the node_modules issue. And as has been repeated over and over in this discussion, there is not a single thing I can realistically do about type errors in third-party packages. For example, there are errors about Dom stuff sinceI'm writing a pure back-end Node.js library module and do not want DOM stuff. My setTimout returns a NodeJS.Timeout, not a number. That's fine with me. I can't control whether authors of sub-sub-sub-sub-sub-dependencies wrote code for both Node.js and the browser. I just DO NOT CARE about these errors.

The argument that it is dangerous to allow developers to exclude node_modules from type checks when skipLibCheck is false makes no sense at all. Not allowing me to do this makes my own code more likely to be broken!

I NEED to be able to type-check my own project's .d.ts files without getting errors about problems in the node_modules folders somewhere-or-other. Otherwise, I simply cannot use skipLibCheck: false which will provide less type safety.

To any TypeScript contributor resistant to giving we developers what we're asking for here, I seriously have to say "shame on you." Stop making it difficult for me to get better quality in my own projects. Stop it. Please. Just stop.

I have work to do. I can't fix the world. I can't put pressure on these third-party packages. I'll lose my job or my contract if I don't deliver. I simply don't have time or energy to help make the world perfect. I learned to program almost 4 decades ago. I like sharp knives. Please give me sharp knives. I know how to use them. I have the scars to prove it, and I don't cut myself much any more. And if I understand a system and can control it, I can heal from the cuts quickly, anyway.

While I wouldn't like it, I'd be content enough if I had to silence .d.ts errors from node_modules on a project-by-project or even a file-by-file basis in some config somewhere. I know what I'm doing. Now let me do it.

I most humbly and abasedly beg you all on bended knee and with bowed head to freaking help me get my job done.

P.S. If you want to do the world a favor, make some non-strict options in subsequent versions of TypeScript no longer able to be off. Just start picking some to be strict always and throw errors if the option is even present in the tsconfig file. It won't hurt my projects.

@danielschnetler
Copy link

Look at this comment

@appden
Copy link

appden commented Feb 23, 2023

I also strongly believe the most pragmatic path forward would be the skip type checking files under a node_modules directory (at least when skipLibCheck is enabled). I haven't seen a compelling argument in favor of continuing with existing behavior that outweighs the pain this causes developers who cannot control third-party libraries.

The workaround I've found for this annoyance is to leverage project references to prevent type checking certain problematic dependencies. The skipTypeChecking utility function (here) returns true for files that are a part of a project reference.

@MicahZoltu
Copy link
Contributor

I'm speculating a bit here, but I would guess that "skip node_modules" would be non-trivial to implement. The compiler has to look at type definitions in node_modules for any dependency you reference, so it has to do some type checking.

I suspect the best that could be done is something like "ignore warnings in node_modules" or perhaps "ignore non-critiral errors in node_modules". This is a bit risky though because it means the compiler would need to make some reasonably guesses about what types it should return in these situations.

@lillallol
Copy link

lillallol commented Jul 15, 2023

So I use TypeScript type checking without the need to compile, i.e. I am manually writing all my types in .d.ts files, and import-type-annotate values in .js via /**@type {import("./path/to/file.js").IMyType}*/. Type checking .d.ts files (both outside and inside node_modules) is disabled when skipLibCheck:true, so I am forced to disable it.

For a project of mine that I use "noUncheckedIndexedAccess": true, and has a dependency another project of mine that does not use "noUncheckedIndexedAccess": true, I start getting linting errors when tsc --noEmit. That is because I also use "maxNodeModuleJsDepth": 8, because I want to make browser compatible imports so that I do not have to bundle during the development stage.

Here is what I did to run tsc --noEmit and ignore all the errors outside node_modules and project root:

import { ts } from "typescript"
import { fs } from "fs";
import { path } from "path";

export const runTscInProjectRootOutsideNodeModules = () => {
    const absolutePathToProjectRoot = path.resolve("./");
    const absplutePathToNodeModules = path.resolve("./node_modules");
    const absolutePathToTsConfig    = path.resolve("./tsconfig.json");
    const parseJsonResult = ts.parseConfigFileTextToJson(
        absolutePathToTsConfig,
        fs.readFileSync(absolutePathToTsConfig, { encoding: "utf8" })
    );
    
    const tsConfig = ts.parseJsonConfigFileContent(
        parseJsonResult.config,
        ts.sys,
        path.resolve("./")
    );
    
    const program = ts.createProgram({
        rootNames : tsConfig.fileNames,
        options   : tsConfig.options,
    });
    
    const diagnostics = ts.getPreEmitDiagnostics(program);
    const diagnosticsInProjectRootOutsideNodeModules = diagnostics.flatMap((diagnostics) => {
        const {file} = diagnostics;
        if (file === undefined) throw Error(); //@TODO I have no clue what this case is for
        const absolutePathToCurrentFile = path.resolve(file.fileName);
        if (
             absolutePathToCurrentFile.startsWith(absolutePathToProjectRoot) &&
            !absolutePathToCurrentFile.startsWith(absplutePathToNodeModules)
        ) return [diagnostics]
        return [];
    })
    diagnosticsInProjectRootOutsideNodeModules.forEach(({
        file: sourceFile,
        start,
        code,
    }) => {
        if (sourceFile === undefined) throw Error();//@TODO I have no clue what this case is for
        if (start === undefined) throw Error();//@TODO I have no clue what this case is for
        
        const fileName = sourceFile.fileName;
        const relativePath = path.relative(absolutePathToProjectRoot,fileName);
        const {character,line} = sourceFile.getLineAndCharacterOfPosition(start);
        console.log(`${relativePath}:${line + 1}:${character + 1}`,`TS${code}`);
    })
    if (diagnosticsInProjectRootOutsideNodeModules.length > 0) process.exit(-1);
}

@matthew-dean
Copy link

matthew-dean commented Aug 29, 2023

@RyanCavanaugh

Can people post "Here's how I ended up importing .ts files from node_modules" or "Here are the errors in a .d.ts in node_modules I had" instead of "same" ?

So, I'm using the chevrotain library. The library has lots of internal types which are very useful for overriding methods that are in external classes but which aren't directly exposed in the public API (.d.ts file).

Unlike some stuff mentioned here, the types from that package are all perfectly good. But, if any of the internal .ts files were to be compiled in the context of my tsconfig.json, they would fail, and compiling those sources is not my intent. My intent was only to consume one defined type, which is here.

I tried importing it directly to my .ts file, which caused compilation errors (invalid types in Chevrotain's source files), because this file imports more than just types. Then, I did this:

// chevrotain.d.ts
import type {
  MixedInParser as ChevrotainMixedInParser
} from 'chevrotain/src/parse/parser/traits/parser_traits'

declare module 'chevrotain' {
  type MixedInParser = ChevrotainMixedInParser
}

This still produced a error. So then, I set skipLibCheck to true. Since I referenced my type ONLY in a .d.ts file, then, according to the skipLibCheck documentation, I understood that type-checking would be skipped in the context of this file. However, that wasn't the case, and I still encountered type errors.

So, while I never imported a source library's .ts file, and only used a .d.ts, it imported source .ts files transitively, which then threw compilation errors, even though skipLibCheck was true and they were imported only in the context of a .d.ts file.

Is there another way to do this? Or is there a way for a source libraries .ts files to be interpreted in the context of its tsconfig.json file?

@RyanCavanaugh
Copy link
Member

@dejayc be constructive.

@hudson155
Copy link

hudson155 commented Oct 22, 2023

I'm a bit confused here.

If there are .ts files in any of my dependencies (such as @auth0/auth0-spa-js), those files are required to conform to the same compilerOptions that my own sources files are? Auth0 does not conform to noImplicitReturns, for example, so it seems like I'm forced to resort to the lowest common denominator and disable compiler options that I want to enforce but some of my dependencies don't conform to.

Am I missing something here?

As luck would have it, despite debugging for hours prior, I figured out my issue 10 seconds after posting this comment. I turns out I was indeed missing something here. I was importing a TS file when I should've been importing a JS file.

Replacing

import { AuthorizationParams } from '@auth0/auth0-spa-js/src/global';

with

import { AuthorizationParams } from '@auth0/auth0-spa-js';

solved my problem! 🎉

Feel free to delete this comment — I just left it in case it helps a fellow straggler.

@ritik3236
Copy link

any solution?
#40426 (comment)

@abumalick
Copy link

I'm a bit confused here.

If there are .ts files in any of my dependencies (such as @auth0/auth0-spa-js), those files are required to conform to the same compilerOptions that my own sources files are? Auth0 does not conform to noImplicitReturns, for example, so it seems like I'm forced to resort to the lowest common denominator and disable compiler options that I want to enforce but some of my dependencies don't conform to.

Am I missing something here?

As luck would have it, despite debugging for hours prior, I figured out my issue 10 seconds after posting this comment. I turns out I was indeed missing something here. I was importing a TS file when I should've been importing a JS file.

Replacing

import { AuthorizationParams } from '@auth0/auth0-spa-js/src/global';

with

import { AuthorizationParams } from '@auth0/auth0-spa-js';

solved my problem! 🎉

Feel free to delete this comment — I just left it in case it helps a fellow straggler.

Thank you very much @hudson155 , this fixed my problem.

@ValentinGurkov
Copy link

I have a monorepo. Similarly to other people here, I wanted to implement a type check step and have run into this issue.
My monorepo package B imports package A as a workspace dependency (pnpm creates symlinks here).
When type checking package B it follows through to package A, which is something I don't want, package A has its own type check step with its own tsconfig.

@akwodkiewicz
Copy link

akwodkiewicz commented May 8, 2024

I have a monorepo. Similarly to other people here, I wanted to implement a type check step and have run into this issue.
My monorepo package B imports package A as a workspace dependency (pnpm creates symlinks here).
When type checking package B it follows through to package A, which is something I don't want, package A has its own type check step with its own tsconfig.

Adding my 2 cents, because I have the same issue with a monorepo and wanted to add more context.

The whole dilemma actually started when I decided to move out of cross-project path aliases (which is considered a misuse of path aliases as stated now in the docs) and rely on proper package imports via Yarn. I first made sure my projects rely on Node16 moduleResolution, and then used the exports entry in the library's manifest, to point "types" to "./*": "./dist/*.d.ts". Wanted to do it the same way as if it was a package downloaded from npm. But that workflow requires to run tsc on your own library just to emit those d.ts files. It works, but it requires a compilation step, even though I have the types already there, just a folder away.

I thought I'll instruct TS to use the library's source code instead and skip the compilation step completely, making the feedback loop instant. So I modified the "types" to "./*": "./src/*.ts".

However, the library was written some time ago, with not-that-strict rules. And now the app that's using it is throwing compilation errors, because it's tsconfig is stricter than the library's.

I now have to either:

  1. invest time in making the old library stricter
  2. make the new app less strict
  3. still rely on additional step of generating d.ts files with the help of tsc, before working with the app -- keep in mind that the additional compilation is not only necessary when writing the code for the new app, but also when running other type-aware tools, such as ESLint -- this makes running linter a whole journey where you start relying on orchestration tools such as Nx, just to define the task hierarchy ("linting app depends on emitting types for all its dependencies").

This variation of the problem is not that severe as the original one, because at the end of the day I am in control of the whole repository. But as you can see you don't necessarily need to deal with faulty packages to encounter this issue!

@devdumpling
Copy link

devdumpling commented May 9, 2024

Just wanted to +1 the above comments.

Not sure if perhaps it deserves a separate issue thread entirely, but like @akwodkiewicz, I have a monorepo (pnpm, turborepo, typescript, Next.js).

Currently I build all of those packages, as well as emit .d.ts for each, and consume them within an app. I have a typecheck step for each of these packages which has worked fine since each package has a types entrypoint pointing to the declaration file generated at build.

However, I no longer actually need to build these packages, as the app is fully capable of transpiling them. Furthermore it turns out the app transpiling is quite a bit faster than running build in each package. This is great, big win. Problem is, now I have no way of typechecking my packages individually (as they all use workspace links and each of them would now point to a .ts or .tsx file instead of the no-longer-generated .d.ts).

Like others above, if I try to typecheck a single package, it will end up typechecking the dependencies of that package as well, which is both redundant (since I typecheck these in separate processes) and potentially buggy, as the tsconfig for various packages may differ in strictness.

I'd love to be able to simply exclude or skip, but that doesn't seem to work (e.g. exclude: "@scope/**") or something.

Are there any other suggestions for monorepo/workspace users running into this issue?

@kumikumi
Copy link

kumikumi commented May 11, 2024

I have a frontend and a backend both written in TypeScript. The backend runs on Bun, and frontend is built with Vite. Using the import type syntax, I'm able to import some types from backend to my frontend code. I have made the decision to not generate or maintain separate .d.ts files, instead I'm just relying on type inference to get the types of values that the backend's API returns to frontend.

This works.

My only remaining problem is that the frontend's build process is now spitting out errors from the backend. It's obviously missing all of the backend libraries, types for Bun etc. In my case, none of it matters, as none of that code is going to be included in the front-end build anyway (because import type lines get erased when emitting code).

Please allow an option to either

A) only show errors from within the project, ie. from files included in the include section of tsconfig.json

(which would make a sensible default IMO, but it's debatable I guess)

or

B) allow specifying an excludeTypechecking section in tsconfig.json, where you can specify a list of directories to be excluded from typechecking. (OP could specify node_modules, I could specify my backend directory to be excluded in the frontend's tsconfig.json)

I respectfully disagree with the notion that the above is "really dangerous". It is no more dangerous than using //@ts-ignore or //@ts-nocheck, which are options that we are trusted with.

TypeScript is a tool - please let us apply it selectively.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Awaiting More Feedback This means we'd like to hear from more people who would be helped by this feature Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests