TypeScript: Support @ts-ignore for specific errors

TypeScript Version: 2.6.0-dev.20171011

Code

function countDown(n: number): void {
    switch (n) {
        // @ts-ignore
        case 1:
            console.log("1");
            // intentional fall through
        case 0:
            console.log("0");
    }
}

Expected behavior:

Ability to make ts-ignore apply to the --noFallthroughCasesInSwitch error but not to other errors.

Actual behavior:

case "1": would also compile.

About this issue

  • Original URL
  • State: open
  • Created 7 years ago
  • Reactions: 597
  • Comments: 128 (17 by maintainers)

Most upvoted comments

I’m repeatedly coming back to this issue. @ts-expect-error doesn’t make sense without specifying the error. I don’t mind that @ts-ignore can’t be narrowed down, eslint’s default rules prohibit @ts-ignore anyway. The problem with @ts-ignore is the implicit cleanup condition. But @ts-expect-error makes a lot of sense a lot of times with narrowing it down to one specific error. Eslint expects me to add a comment as well. But expecting any error makes the flag unsafe. So what’s the point then?

Having said this, all the places where I see a reaction from a TS representative, I see wrong arguing. Focusing on @ts-ignore, bringing in short names (why?)…

“We don’t want people to get into a situation where upgrading from TS X.Y to X.Y+1 yields hundreds of new errors simply because we changed an error message code”

I see 3 problems with this (at least):

  1. It’s arguing with extremes. Having hundreds of @ts-expect-error on one particular error type in a codebase feels like a fishy case, but quite uncommon at least. And even then, I don’t see a problem with search-and-replacing those comments. Also how common is that TS changes an error code for the same error condition? Has it even ever happened?

  2. Also, narrowing down @ts-expect-error is the developer’s responsibility with all the consequences.

  3. It’s indeed counterintuitive that error codes are tied to the error text. Why is that? Nobody expects that errors are defined by their text. Everybody treats the text as prone to being changed over time. That’s why there’s a code. On the other hand, introducing a new code for the same error already adds inconsistency to the global TS knowledge - think about googling new error codes not matching the results for the old one; and then maintaining a code history on all the blog posts and help pages on the internet, which explains a particular error…

The only actual argument, if I’m not mistaken, is the instability of the error codes.

A couple thoughts

  1. I really hope your team did something to standardize error codes between versions; the fact that there was any resistance to that idea is itself disturbing. It is the more forward compatible option that gives the most flexibility in the long run even if you end up with a bunch of unused codes. It may be hard, but who cares about hard; if we wanted easy we’d all be using square space and not coding our own stuff in typescript.
  2. There have been very compelling arguments for the utility of refined expect-error or ts-ignore entries.
  3. noIplicitAny and other tsconfig linter options are already examples of this capacity except they use names rather than codes; the advantage of the codes is that they can be linked to definitions that are internationalized.

All we are asking for is the ability to ignore those errors that we deem to be more style than error especially when the code actually runs, yet not have to not turn everything off.

The general reply in effect is that it is a bad idea to turn off errors but if you have to then turn them all off which makes no sense at all whether at the global or individual line level.

What would the actual level of effort be to implement ignorable/expectable error codes at the comment AND tsconfig level for consistency?

If it were implemented, would the folks in this thread be ok with the ts compiler generating a report of ignored errors that could in some way (via opt-in program) reported back to the ts dev team so that they can have some real world feedback on what all is getting ignored?

My problem with suggestions like “we are not seeing issues with …” is that you aren’t looking at any real code. You are waiting for the rarified people who know enough to complain, and with enough time to, to actually complain. To make those kind of arguments you need real feedback but if you are making it so we can’t know what the errors we’re ignoring are, except all of them, then how can you possibly know that your errors are useful or what the consequences of your choices are?

I’m thinking there is an opportunity for a meaningful feedback loop and a win-win if you let us codify our overrides inline with some metric info going back to you rather than have to come here to complain about things,

// @ts-ignore TS/1234 because I know what I’m doing Seems a lot more reasonable than // @ts-ignore developers Which leads to // @ts-expect-error typescript devs do not care so neither should we

The other big argument is error code specific ignores on a single line.

Suppose you want to ignore two specific errors on the same line, you should be able to // @ts-expect-error TS123, TS2345 only those two kinds of errors. If at a future date, a new error pops up on the same line but of a different kind, you don’t want to have that ignored.

Having a more granular “hold the line” functionality would be a massive boon.

👍

This would also be very useful for our team, we are slowly migrating a large JS app to TS. We’d like to turn on strict compilation flags (especially --noImplicitAny and --strictNullChecks) but have lots of deviant files that need fixing.

Specific error suppression would allow us to gradually migrate everything to strict compilation.

Any plan for this to be implemented?

Unfortunately this discussion often assumes a new TypeScript Code base and forgets all “legacy code bases”. Especially during the migration from JavaScript to TypeScript it would be very helpful to clearly define what error has (temporarily) to be disabled. I had to migrate a very lathe JavaScript application to TypeScript and it would have been very helpful to only disable one specific problem instead of hiding others when incrementally improving the type sanity.

What about a syntax like this:

Ignore all errors (current)

// @ts-ignore -- My reason for this.

Ignore one error

// @ts-ignore:7029 -- My reason for this.

Ignore multiple errors

// @ts-ignore:1248,6133 -- My reason for this.

For #19573, you could use @ts-ignore-start (also with an optional list of error codes) and @ts-ignore-end.

Edit: The -- My reason for this. comments are of course optional (but recommended).

Following

Today: “Why let perfect stand in the way of the good? Just do something right away, I’m sure it’ll be fine”

Tomorrow: “Yes, that new proposal would be more ideal, but we can’t change the behavior now” / “Well why did you rush the feature instead of figuring it out first?”

The day after: “Why is this language so full of inconsistencies? It’s like the designers just rushed something out the door to satisfy people. Pretty disappointing that they weren’t willing to take their time to do it right.”

Seconding (and wanting to track this)

This would be super helpful in our case as we have a class with a bunch of variables, and a function that accesses them by key value (vs direct getter function). As a result, they are never called directly within the class and are throwing the error despite being necessary to the functionality. I don’t want to have to disable the “noUnusedLocals” check across the board, just in the two classes where this is the case.

Edit - It’s been 2 years and a job change, but I wanted to clarify something bc I’m looking back at old issues and realized this description is slightly unhelpful My use case for this request was more along the lines of this introduced feature https://github.com/microsoft/TypeScript/pull/33383 But I imagine I did not want to ignore every rule in the file, so wanted to be able to specifically turn off the rule that wasn’t preventing working code in an edge case, but we didn’t want becoming common practice through the code base

Especially when migrating to TypeScript it is necessary to suppress some of the error messages reported by the TypeScript compiler. Currently TypeScript offers a very rudimentary way to do so and it could be improved by using a syntax similar to lint tools like tslint and eslint by at least offering the following inline options:

  1. disable or enable warnings for specific rules //@ts-disable TS1234 TS1235 //@ts-enable TS1234 TS1235

  2. disable specific rules on a specific line //@ts-disable-line TS1234 TS1235

  3. disable specific rules on next line //@ts-disable-next-line TS1234 TS1235

It’s actually absurd that this is even still an issue. I’ve experienced a problem related to this. The underlying cause was due to a typescript change in 2.4, as discussed here:

https://github.com/ag-grid/ag-grid/issues/1708

We were then left with three options

  1. pin typescript to some version < 2.4
  2. update our code to allow updating ag grid to a compliant version
  3. fork our own version of ag-grid until we can upgrade

option 1 is not preferable, as it means we can’t leverage any new typescript features and can result in having mismatched dependencies

options 2 and 3 are possible (although difficult), and they really only handle one such specific ‘error’ preventing successful compilation.

Why is there not an option for us to ‘ignore’ TS2559 (as an example)?

Would love to see this. I was actually a bit surprised that there wasn’t already support for this.

There is prior art to use as inspiration in eslint. Things like this:

// eslint-disable-next-line @typescript-eslint/no-unused-vars
const unusedVar = 'hello';

Would love to see this implemented for @ts-expect-error and @ts-ignore. 🙏

I’m not trying to be overly pedantic here, but Typescript isn’t a “linter”, it’s a type system. I asked for an example because, when you ts-ignore an error, 9 times out of 10 what you’re doing under the hood is basically an any-cast – you tried to do something that the type system doesn’t allow, and by ignoring the error and doing it anyway you’re telling TS not to enforce types on that line.

That’s exactly correct. But Typescript is also an incremental type system. It is explicitly designed to allow gradual migration from Javascript. If you’ve ever tried to migrate a 500k line plus codebase to Typescript, you’ve probably noticed that you can’t do it all in one go. You have to work incrementally, which means you have to allow any into your codebase. It makes a great deal of sense to allow developers to make specific type violations in narrow contexts. ts-ignore-error and friends are intended for use in hybrid codebases as an aid in migration. Allowing them to target specific errors would better facilitate that purpose.

I’m not ust “throwing in ts-ignores everywhere”. I’m putting them in 3 specific locations where problems occur. The problem happens due to an interaction between TRPC and Prisma - particularly where using zod’s optional() behavior (for example, when you expect a type of z.number().or(z.array(z.number()).optional(), and an undefined value gets passed to a through this to a prisma method, it tries to coerce it to null. I haven’t opened an issue, or bothered to spend an hour making a PR and/or a fix, because it frankly is not worth my time, and it’s not my project.

Like I said - the purity of not having to do this is not worth it. Whether or not I opened an issue or wasted my time on a reproduction repo is not the issue. The issue is, I should be able to ignore a linter error when I know it’s not relevant. The linter is there to help me, not hinder me.

For this feature, if it wasn’t thought/said before, I would be expecting something like // @ts-ignore TS1801 to ignore only a specific warning.

I also don’t like the @ts-expect-error wording; it feels like I am writing this in tests. I’d prefer, instead, to have an option to make @ts-ignores act like @ts-expect-error instead.

Or even better we can have

// @ts-expect-error TS2445 ts1942 Ts552

@RyanCavanaugh As a possible use case for this feature. I’m responsible for a pretty large (~100000 loc) project. During our incremental migration from JavaScript to TypeScript we had to use ts-ignore quite a bit and sometimes (by ignoring a complete line) we missed a problem that was accidentally hidden because ts-ignore ignores all errors. The option to restrict ts-ignore to the specific error that “must” be ignore could have prevented quite a few problems during the migration.

Also, I’m not going to go through the initial expect-error PR threads, but during these debates were you on the side of not adding expect-error at all?

Consider the fact that ts-ignore doesn’t error on a “missing” error, though we could have easily made it do so. This was a very big discussion at the time we added that feature, with very similar characteristics to this one, and ultimately we went in the direction that would minimize upgrade pain. I think adding ts-expect-error before ts-ignore would have been a huge mistake; doing it in the order we did, with a long gap between, cemented ts-ignore as the “default” way to suppress an error, and then later having ts-expect-error meant it was fairly clear that you were opting in to a very specific behavior with reasonably foreseeable caveats.

I don’t see the same analogue here, because it’s not at all obvious that error codes are not necessarily stable between versions. C# has to do a ton of backcompat engineering to make sure their warnings are consistent version-to-version and we don’t want to do that (though people are already speculating about how we should implement it). If we add this feature, we are guaranteed going to stoke ire from engineers who go all-in on it without realizing this, and there’s nothing I see that can prevent it.

Maybe that future ire is worth it for the sake of whatever people are experiencing here, but frankly I have not seen the evidence for what kinds of problems people are encountering that this feature would solve better than other existing solutions. People just keep going in circles asking why it doesn’t exist.

I get that it feels bad to ts-ignore some line of code that might acquire some additional error in the future, but it’s instructive to read the original ts-ignore thread (which we also pushed back on for a long time) where 95% of people said they “needed” the feature for suppressing some particular error that they could have suppressed with a more tactical as any. We added ts-ignore with the intent that it be used for the remaining 5% that can’t be suppressed by any existing type system mechanics; in this light I think the concern about oversuppression is somewhat misguided because there should be very very very few ts-ignores in your codebase, and because so few constructs require a ts-ignore, it should be very unlikely that they acquire additional errors later. I can’t reason people out of their worries but we have to weigh “dev X will sleep better at night” vs “dev Y is facing an error cliff during upgrades because dev Z told them that they should always ignore specific error codes instead of using as any

@RyanCavanaugh If I had hundreds of ts-ignores in my code, I’d definitely want them to have TScodes associated with them, to restrict their error-hiding radius.

But in the few(?) cases where the.TScode kept changing, I might revert to a simple comment to avoid churn.

The situation seems analogous to eslint, where users are already accustomed to updating their ignores as the error types subtly change. Are ts-ignores fundamentally different from eslint-ignores in this regard?

Today: “Why let perfect stand in the way of the good? Just do something right away, I’m sure it’ll be fine”

In all fairness, any decision made today or tomorrow wouldn’t be “right away” considering this issue was opened in 2017…

The day after: “Why is this language so full of inconsistencies? It’s like the designers just rushed something out the door to satisfy people. Pretty disappointing that they weren’t willing to take their time to do it right.”

Full? Are there really people here who sound like this?

Is there intent to make it so that tsignores can be flagged as errors when the following statement has no error to ignore? For example, I’m adding a tsignore to ignore an error that is expected to be fixed in a future version of typescript (#26235), but would like to be reminded to remove that tsignore once typescript is capable of handling it normally

@minecrawler @ts-expect-error is already something that exists, it just can’t be targeted to specific codes currently.

It’s, IMO, often preferable over using @ts-ignore when you encounter something that you have to work around for now but should be fixed in future TS/dependency versions as both directives have some level of danger (being a global error ignore for the subsequent line).

Having the @ts-expect-error error once the problem is fixed (by seeing Unused '@ts-expect-error' directive.(2578) later) means you get rid of the dangerous error silencing behaviour in your codebase sooner.

Contrived but simple example:

// v1 of some module I depend on - their function is typed wrong, it says it takes string not number.
// Assume these two calls are in different parts of a big application:
someFunctionFromAModule(4) // Argument of type 'number' is not assignable to parameter of type 'string'.(2345)
someFunctionFromAModule(4) // Argument of type 'number' is not assignable to parameter of type 'string'.(2345)

// So, we add ts-ignore/ts-expect-error and report the issue to the project owners
// @ts-expect-error types wrong
someFunctionFromAModule(4) // No error
// @ts-ignore types wrong
someFunctionFromAModule(4) // No error

// Upgrade to v2 and they fixed the typing issue, we get prompted about the ts-expect-error no longer applying but not the ts-ignore
// @ts-expect-error types wrong
someFunctionFromAModule(4) // Unused '@ts-expect-error' directive.(2578)
// @ts-ignore types wrong
someFunctionFromAModule(4) // No error

// So we remove the ts-expect-error but forgot about the ts-ignore elsewhere in the codebase
someFunctionFromAModule(4) // No error
// @ts-ignore types wrong
someFunctionFromAModule(4) // No error

// Later we upgrade to v3 of the module, but didn't notice they'd renamed this function
// Since we were prompted to remove the ts-expect-error when our original issue was cleared we have now been prompted that this function doesn't exist anymore
someFunctionFromAModule(4) // Cannot find name 'someFunctionFromAModule'.(2304)
// But, because this one still has a ts-ignore on it we forgot about our code just dies when it reaches this point
// @ts-ignore types wrong
someFunctionFromAModule(4) // No error

Yes, unit tests, e2e testing etc could also pick up the second call’s problem but the choice of @ts-expect-error over @ts-ignore allows us to pick up the issue earlier in the process.

Another case where disabling specific error is useful is refactoring large codebase. Imagine we want to set noUncheckedIndexedAccess to true in our tsconfig. And turning this flag on reveals ~1.5k errors. Fixing that much errors in one PR is hard and dangerous. Possible solution is to mark all errors occurrences with @ts-expect-error with specific code (which is safe to do and can by done with a script, not manually) and then refactor errors iteratively.

I’m really looking to this feature and it really important IMO

(I’m sorry if it’s already been said…)

[…] Suppose you want to ignore two specific errors on the same line, you should be able to // @ts-expect-error TS123, TS2345 only those two kinds of errors. […]

The problem with using error code numbers (e.g. TS123) is that the reader is not able to understand from the code what the error we’re expecting, I think an ESLint type of ignoring errors would be much more user friendly (e.g. // eslint-disable-next-line no-alert, quotes, semi)

It would be most interested in this features and do not fully understand why it should be a bad idea. Clearly changes in the reported errors might require changes in the code using them but at the end it is up to the user of TypeScript if he disables all errors or just a specific one. On the other hand I currently have to deal with the fact that I repeatedly hat to deal with code that was expected to allow one specific error but ended up hiding other problem in the same line.

error TS2445 …

when tsc reports a single error, can ask ignore a single error only? e.g.

// @ts-ignore TS2445

@RyanCavanaugh if I understand correctly, your main issue is with the fact that TS could give a different error code for an issue in the future causing upgrade pains? However every TS version is already assumed to be a breaking change, so why not give users the ability to choose how much the ignore radius should be?

AFAIK the proposal is the following

@ts-ignore(?<comment> .*)? - Behavior remains as is Ignores the error on next line, if there is no error, tsc doesn’t complain. @ts-expect-error(?<comment> .*)? - Behavior remains as is. If there is any class of error on next line, error is ignored. If no error, then tsc says Unused '@ts-expect-error' directive.

New proposal @ts-expect-error (TS\d+): (?<comment>.+)? - Expecting a specific error and a reason why it’s ignored. If in future there is a different error code, then user has to explicitly acknowledge that and change it.

Basically we’re saying, let us decide how strict we want our ignore radius to be. In some projects, going yolo @ts-ignore makes sense, in some projects we want to tightly control ignored errors and slowly work on reducing the number of ignored errors. Having a smaller ignore radius is a big boon.

Every TS version upgrade is already a breaking change. There has been exactly 0 times, where a TS upgrade has just worked for me for our 100k+ lines codebase. So if you’re fear is introducing more errors for a version upgrade, this hasn’t been a big concern for us personally. Fixing @ts-expect-error issues with a new TS error code is a simple find replace. That’s an easy class of errors to fix. I actually want to know when that happens and explicitly fix each one rather than it happening transparently.

Also this issue is something I feel strongly about and would be happy to champion a PR if Typescript core members agree it’s something they are willing to accept.

This sounds largely like the need for a perfect solution is preventing a good solution. I can think of multiple responses to your specific argument, but I don’t really want to get bogged down in arguing on one specific thing.

Offhand, I’ve been through multiple migrations where they’re much simpler this sort of one-off error squashing. Start with tooling to ignore the current errors so we can move forward writing new error-free code with the codes enabled. Is there a better path to enabling “strict” mode? Or say enabling new error codes in the future?

I really like that Flow forces me to ignore specific errors:

< // $FlowFixMe[incompatible-variance]
< // $FlowFixMe[incompatible-type]
< // $FlowFixMe[prop-missing]
> // @ts-ignore: Uhh.
foo(bar)

Slightly unrelated, but would be better if errors could match offending lines more closely:

> // @ts-ignore: Can't assign number.
process.env = {
  FOO: 'bar',
<  // $FlowFixMe[incompatible-type]  
  QUUX: 1000,
}

I am not a Typescript expert, so I’ll not add arguments of the previous debate.

However, I would like to express the need of this granularity with an example.

I use react-navigation that allows us to declare routes with Typescript:

export type MyStackParamList = {
  Index: undefined;
  Show: {
    id?: string;
  };
  Edit: {
    id: string;
  };
};
const Stack = createStackNavigator<RidesStackParamList>();

You may define multiple stacks like that, but I will not going to details here.

Using the navigation to change the current route is supposed to be limited to the current stack, so if I do:

navigation.navigate('Payment');

Typescript will throw error TS2345 because the Payment parameter does not match the related MyStackParamList but an another stack.

However, this code part works like a charm. You can navigate to an another route of an another stack without issue

Is it me wrongly using the library? Maybe, surely. It’s classified.

However, supposing it’s an issue related to the vendor type definition, I can do anything waiting the fix than ignoring the error.

But as @andy-ms said, I would like to be sure only this error will be silenced and avoid possible other mistakes on the same line.

If specifying errors to ignore is dangerous according to some people here, what are the alternative for this case?

Thanks for reading

I’m also coming from ESLint. I love the way they made their error suppression intuitive to read. Take this example:

if (e.error) alert('Error!'); // eslint-disable-line no-alert

This comment clearly states that for this line only, it’s okay to use the alert function. Compare that with an (imaginary) numeric error code:

if (e.error) alert('Error!'); // eslint-disable-line E0815

Unless I learn those error codes by heart (and I’m really bad at memorizing numbers), I’ll have no idea what this comment does. Maybe e.error is a private property that I shouldn’t normally access. Maybe I’ve omitted mandatory curly braces. This line may contain any number of problems. Reading the code, I can’t really tell what is being disabled here.

Without fine control about how legacy files are handled it is really hard to introduce new typescript rules into a project.

Please give this use case some more consideration.

@jlengrand you could use a generic // @ts-ignore comment. It already works and ignores all TypeScript errors on the next line. This issue is only about enhancing the feature to enable targeting more specific errors.

Personally, I find entire TSC errors to be, without explicit rationale behind them, 100% useless. Consider TS18022, why is that even an error? Or TS2775. Or TS18019. I legitimately had made a class that used a static, private class method, that asserted and determined whether or not something was genuinely from that class.

Oops, I broke at least 3 TS rules at once 😃 No static private, no private methods, no assertion object methods. None of these are poor coding practice, they aren’t inherently unsafe, they can be statically analyzed, but they are denied by TS, which prevented me from porting my JS to TS.

There are plenty of errors that are just that, errors, they should make someone stop and think about whether what they had just written was unsafe, and rewrite it, but the “errors” that I would like to disable en masse fall into a whole different category outside of safety and linting, into the area of “why is that an error in the first place?”

@RyanCavanaugh I get the perspective of backwards compatibility and maintenance of old code being a burden.

In my specific case I’m running into a problem related to immer losing symbol keys which I believe is caused by keyof removing well known symbols. For this case I have to use a ts-ignore statement. I think adding some compiler options would really help because ideally I want to remove these ts-ignore lines once they are no longer needed, or in a perfect world pay attention to different ts exceptions that pop up on the ignored line.

I think for ts-ignore having an explicit opt-in typescript compiler option that would throw an error if there is no typescript error on the line would be useful.

I think the same approach where specific error ts-expect-error-#### statements would fall back to the general ts-ignore would work. You can have a compiler option which specifies whether you want the fallback (by default it is true), effectively making ts-expect-error-#### be an explicit opt-in.

Even if you don’t opt into either of these behaviors you have the ability to periodically run a build with these rather than checking each line manually.

@RyanCavanaugh Question for you about your comment here:

The real problem (I’m surprised this didn’t make it into the notes) is that we generally don’t consider a different error message being issued as a significant breaking change.

For example, we sometimes add a more-specific error message for certain cases to give a better developer experience. Every error message has its own distinct error code.

We don’t want people to get into a situation where upgrading from TS X.Y to X.Y+1 yields hundreds of new errors simply because we changed an error message code, or end up doing a bunch of Rube Goldberg work in the checker to issue a “legacy” error message in some cases.

I’ve run into this problem with error codes in HTTP APIs, and the relatively simple (non Rube Goldberg) solution was: instead of each error having a single code, it has a list of codes. So, if the API originally issues a general error (e.g., type/code: "ValidationError"), and then is later refined to issue something more specific, the new error has the original error’s type/code in its list of codes (e.g. ["ValidationError", "PasswordToShortError"]). Then, any program that was checking for the old code will still see the new error as an instance of the old code. Perhaps a similar approach could be taken here?

For backwards compatibility, every TS error might continue to have a primary code, but also a “parent code”. New, more-specific errors would be defined with the previous, more-general error as the parent code, and the compiler would continue to always issue the most specific error, without that causing breakages. Only @ts-ignore would make use of the parent codes (ignoring the error if it or one of its parent’s has the error code the user asked to ignore).

Perhaps the parentCode field could be named something like “derived from”, to get away from the appearance that the error codes form a complete/strict hierarchy (since presumably all existing errors would be left as-is, with no parent code). Or, parentCode could be replaced with an altCodes array in case there are times when multiple previous errors now manifest as one newly-introduced error.

Does this solve the issue with breaking changes?

It doesn’t address the case where the error issued in TS vX.Y+1 genuinely new/unrelated to the error issued previously — but, in that case, it’s not clear that the @ts-ignore should apply, because TS may truly be catching something you didn’t want ignored. My motivation for wanting to ignore specific errors is that I don’t want t lose all type checking on a line just because I need to ignore one error (e.g., to work around a TS limitation), so, for that, not ignoring newly-introduced-but-unrelated errors is definitely a win.

Re requiring string error names, instead of numeric codes: I don’t think that should be a blocker, because a numeric code provides the reviewer with strictly more information than the bare @ts-ignore we have today. At least a reviewer can look up a code from their browser, whereas trying to know what a plain @ts-ignore is for (without a comment) actually requires cloning the code, removing the @ts-ignore, and seeing what the compiler complains about.

We did (most of) a typescript migration and are now going through a major refactor. We would really like to enable strictNullChecks and noImplicitAny to ensure the quality of our refactored code going forward. However, our codebase has ~1500 errors for each rule and we aren’t going to be able to fix all of them immediately. It’s worrisome to completely ignore 3000 lines of code and we would much prefer to ignore only these specific errors.

Please consider adding this feature.

@benkeen to be clear, neither @ts-ignore or @ts-expect-error operate at the file level. @ts-nocheck does.

If @ts-expect-error accepted one or more tsErrno tags, as so many of us are hoping for, then you could parse the eslint output and precede every offending line with an expect-error comment.

By the way, not to make light of your situation, but disabling all the strict checking might reduce your issue to one that is easier to manage.

@nojvek These things are all true qualitatively, but there is a quantitative aspect that needs consideration. We totally expect that most very large codebases will see a dozenish breaks during version migrations, and that the maintainers of those codebases look at the breaks and mostly say “Yeah I can see how that was sketchy”.

So if you’re fear is introducing more errors for a version upgrade, this hasn’t been a big concern for us personally

The reason you haven’t had this problem is precisely because we’ve avoided adding features (like this one) that would create that scenario. I don’t know what more to say on that; not getting wet in a rainstorm is a bad reason to throw away one’s umbrella.

Here’s a scenario that is entirely foreseeable:

  • Engineers at a large company with a TypeScript monorepo see this new feature for explicitly ignoring specific errors and “upgrade” their ts-ignores (which were inserted by an automated upgrade tool) to ts-specific-ignores in the name of “Now we know what’s going on!”
  • This continues for a few years, and because TS generally issues either “X not assignable to Y” or “Can’t call Z” errors at the top-level, those two error codes are everywhere
  • We find a neat way to be more specific in half of these errors, which necessitates a new error message and message number
  • Feedback on TS 5.7 is they’re blocked on upgrading because there are so many new errors in this version that it’s impossible to tell if anything bad is getting through

You’re acknowledging the danger of this feature, saying that you accept the danger, and therefore there’s no problem with the feature existing. The problem is, we’re not just adding the feature for you, we’re adding it for everyone, and there’s no way to put a EULA sticker on this that says “FYI, version to version upgrades are totally YOLO if you use this”. They’re going to see it on Stack Overflow, stick 40 different ts-specific-ignores (which feel “safer” than a blanket ts-ignore) in places where they should used a tactical any instead, get burned on version N+1, and complain about how TS sucks at versioning and broke their app with no warning.

The downstream effects of “I accept the danger” thinking can already be seen with ts-ignore: people misapprehend what ts-ignore does, thinking it will somehow appear in declaration files. Or that they can use it to ignore TS telling them about a problem that will prevent correct emit. ts-ignore is a “last resort” feature and I would really argue that any line of code for which the ignored error is not immediately apparent by syntactic form alone is a red flag. There’s little upside IMO to expanding its use or encouraging it to be more brittle unless there are use cases that simply can’t be solved other ways.

@thw0rted My impression is: if a more recent TS version is really catching a new kind of error, that was previously no cought because of an oversight or language limitation, it should never be suppressed by old error codes being ignored. IMO that is the whole point of this feature rquest.

Your example is not really a new error, it is an augumented way of dealing with an old error. I undersntand that, the way things work now, it would be required to use the new error code TS7632, thecnically making it a new error. But, this is not the true nature of this thing.

Would it make sense to separate them into what is an error and what is a smart suggestion to solve this error? In that case, we would have preserved the original error TS2339, which is not in fact obsolete. And there would be a list of possible smart suggestions for each error code.

Tossing my hat into the ring: I’d really like to ignore TS1206. We have an in-house transformation plugin that’s using decorators outside of the currently accepted scope. This is the only instance in which we’re violating the sacred rules, but it’s super handy for injection in our use case. At present we’re having to use the all-encompassing // @ts-ignore and that’s no good for catching other issues.

how would expect-error work? There needs to be an error else it doesn’t build? That sounds dangerous, do you have a use-case? I’m not sure if that wouldn’t be out of scope of the original request

I’ll be adding this check to my extension, but the release timeline is somewhere between a long time from now and never so you can use ts-morph and something like this code if you really want to check that casts are handling specific error codes

function checkIgnoreTSC(project: Project) {
   project.getSourceFiles().forEach((file) => {
      console.log(`checking ignore TSC for file ${file.getFilePath()}`);

      file.getDescendantsOfKind(SyntaxKind.AsExpression).forEach((expr) => {
         const castText = expr.getTypeNode()?.getText() ?? 'no type node';
         const holdText = expr.getFullText();
         const exprText = expr.getExpression().getFullText();
         console.log(castText);
         const exprLineNumber = expr.getEndLineNumber();
         if (castText.match(/^ignore_TSC[0-9]+$/)) {
            const code = castText.slice(10);
            const replacedWith = expr.replaceWithText(
               `${exprText} /*${castText}*/`
            );

            const err = file
               .getPreEmitDiagnostics()
               .find(
                  (diagnostic) => diagnostic.getLineNumber() === exprLineNumber
               );
            if (err === undefined) {
               console.log(
                  `No error found at line: ${exprLineNumber} ${castText})`
               );
            } else {
               const errCode = err.getCode().toString();
               if (errCode !== code) {
                  console.log(
                     `Incorrect error ${errCode} vs ${code} at line: ${exprLineNumber} ${castText})`
                  );
               }
            }
            replacedWith.replaceWithText(holdText);
         }
      });
   });
}
type ignore_TSC1234 = any;
type ignore_TSC2322 = any;

const no_error: number = 1 as ignore_TSC1234;

const should_mismatch: string = 1 as ignore_TSC1234;

const should_match: string = 1 as ignore_TSC2322;
 
ignore_TSC1234
No error found at line: 4 ignore_TSC1234)
ignore_TSC1234
Incorrect error 2322 vs 1234 at line: 6 ignore_TSC1234)
ignore_TSC2322

For both TS18022 and TS18019, your linked issue points out that private methods only reached “Stage 3” quite recently and TS hasn’t gotten around to supporting them yet. Likewise for other up-and-coming ES private features (static #foo, etc). The good news is that TS legend and all-around swell guy @dragomirtitian says in https://github.com/microsoft/TypeScript/issues/39066 that support is inbound soon.

For TS2775, the linked issue (and a PR that is linked several times from there) make it pretty clear that there’s a design limitation for control-flow analysis where you have to basically “re-type” the assert function when it’s imported. (I agree with what appears to be the general consensus that the error message could be clearer, but at least searching for that error number brings you to those discussions.) This is irksome but not insurmountable.

@RyanCavanaugh Then why have @ts-expect-error at all? If we don’t want people abusing things then why were per-line @ts-ignores added back in 2.6 instead of having only global no-checks?

I admit the second example is a bit contrived; but for my first question, the documentation for ts-expect-error seems to be in direct conflict with the (good) points you were just making.

I feel conflicted, because I agree with the points you’re making, but agree with the points made in the 3.9 documentation, and would be valuable to our team.

@RyanCavanaugh how would you respond to these excerpts:

Unfortunately if our tests are written in TypeScript, TypeScript will give us an error! … That’s why TypeScript 3.9 brings a new feature: // @ts-expect-error comments. When a line is prefixed with a // @ts-expect-error comment, TypeScript will suppress that error from being reported; but if there’s no error, TypeScript will report that // @ts-expect-error wasn’t necessary.

Given this logic–that it’s better to say expect-error than ignore in this type of test–then why isn’t it EVEN BETTER to specifically say which error you’re expecting?

Pick ts-expect-error if:

  • you’re writing test code where you actually want the type system to error on an operation

By adding a specific error code, instead of just blindly ignoring the line, the error can be PART of the test itself.

In addition, many of the points that you bring up (that I agree with) are similar to the ones given in this article for picking ts-ignore:

Pick ts-ignore if:

  • you have an a larger project and and new errors have appeared in code with no clear owner
  • you are in the middle of an upgrade between two different versions of TypeScript, and a line of code errors in one version but not another.
  • you honestly don’t have the time to decide which of these options is better.

You’re right that a danger of adding specific errors is that you will use it to squash random errors in a codebase (point 1). Another danger you point out is how this can be abused and bite people in the future when updating (point 2).

From what I gather, having specific error codes amplifies the benefits listed in “Pick ts-expect-error if:”, and exacerbates the problems in “Pick ts-ignore if:”.

Just wondering what you think about this. Also, I’m not going to go through the initial expect-error PR threads, but during these debates were you on the side of not adding expect-error at all?

@RyanCavanaugh I would agree with you on most design decisions but I think this is different because I don’t think there is a “perfect” solution and silently ignoring possible errors does also not seem like the perfect solution. For this problem I would rather aim for a practical compromise.

For something that’s been open 2 and a half years, it’s hard to tell what’s actually blocking this? Is it just that it’s not defined well enough? Is there a specific technological limitation? Or is it just the time hasn’t been put in (or isn’t available)? Just curious, as I too would love this and fully agree that it allows a much simpler path to enabling strict mode.

This would be great. Here’s how I work around the limitation:

        // @ts-ignore: access protected property
        scene._cssLayer
            //
            .removeChild(sceneState.renderer.domElement)

That way, I know that ts-ignore is being applied specifically to the scene._cssLayer access, which is a protected member (it is fine for my library to access “package protected” members). I don’t want it to hide any other errors in how I’m using the following removeChild method.

I also use Prettier, so I have to add the extra // comment so that Prettier won’t convert

        // @ts-ignore: access protected property
        scene._cssLayer
            .removeChild(sceneState.renderer.domElement)

to

        // @ts-ignore: access protected property
        scene._cssLayer.removeChild(sceneState.renderer.domElement)

so as to avoid unintentionally hiding other errors.

EDIT: I just realized that the following works too, so prettier won’t mess the formatting:

        // prettier-ignore
        // @ts-ignore: access protected property
        scene._cssLayer
            .removeChild(sceneState.renderer.domElement)

@bmeck,

What did you think of my suggestion?:

I think a good compromise would be to support numeric codes for all errors, and short strings for simple/common errors like unused-parameter.

I really don’t think that it’s practical to have string-based codes for all of the 1000+ errors that may occur, and in many cases they won’t even convey enough information anyway.

Your IDE could display the error information when hovering over numeric error codes.

I only want to ignore strict null checks in a tiny number of cases where they pose a problem. If the type is number | number[] | undefined, I still want it to throw an error when someone passes a string.

I don’t think this feature would help, then, as errors aren’t that granular. undefined is not assignable to number is the same error as string is not assignable to number. If you’re specifically trying to bypass a null check, just write foo!.

This is something that would highly valuable to us. Having a ts-expect-error should always be a last resort, but if it comes to that then I would at least like to limit the scope of it as much as possible.

Just to be clear, the issue here the future-upgrade scenario. Let’s say you write something like this in hundreds of places around your codebase:

// @ts-ignore: TS2339, TypeScript is wrong about `href` not existing on `event`
const elementIdName = event.target.href.split("#").pop();

Then we improve the error message to be

 TS7632 Property 'href' exists on 'HTMLAnchorElement' but not 'HTMLElement'. Are you missing a type assertion?
 ^^^^^^

The error code is going to change because there’s a 1:1 relationship between error codes and messages. Now what? A routine compiler upgrade that should only introduce a handful of errors is now giving you hundreds of errors with a tedious process for fixing your ignore comments. That’s really bad and we’d like to not paint people into that corner unless we absolutely have to.

You can fix everything with more tooling but at some point the cognitive burden doesn’t outweigh the benefits.

One option to solve the problem of not being able to tell what the error code means, would be to have the current description of the error shown as a vscode tooltip when the developer passes the mouse over the error number. Not sure how hard it would be to coordinate this with the vcsode dev folks, but it’s an idea.

I’d been talking to @orta about what it looks like to have an easier path to strict type checking from a project that has no strict checks enabled. I feel like this feature is a key item that could be added to help us get there.

I’m 👍 for just using error codes. Agree with @DanielSWolf above that you’re not always in an environment where you have a rich IDE, but if the TS site exposed a simple url based lookup of error codes (i.e. https://www.typescriptlang.org/error/TS123) then it’d be easy enough to deal with. Short codes for common errors would be nice, but just having something would be better than where we’re at.

Looking at the last meeting notes around this topic, I do see the challenges. I feel this comment by @RyanCavanaugh does a good job of conveying one of the largest challenges: Namely error codes go from being just useful for debugging to potentially something that could cause new type errors between versions.

It’s a hard problem.

Another potential option would be to provide a customizable error map. I’m not convinced this is the best idea myself, but it’s an option…

i.e. in the config

{
  "errorCodeMap": {
    "implicit-any": ["TS123"]
  }
}

then in the code

// @ts-ignore implicit-any
foo(bar) { }

then if something broke between updates you could just update your own short-code mapping. That would at least give people the ability to express intent through the ignore mechanism, though the implicitness of the short-codes and the ignore comment is unfortunate.

@bmeck

Editors have all sorts of hover/code lens/goto enhancements, this would allow more in depth documentation to be linked to in both cases. If your editor cannot support such workflows, uncommenting the line would also reveal the longer error message.

I spend quite some time reviewing pull requests on GitLab. Similar to GitHub, their GUI is great for looking at diffs and commenting. But it’s no full-blown IDE, so there are no handy tool-tips or interactive compiler messages. More generally, I use a number of tools for working with source code, and I can’t always rely on smart IDE features.

This is curious to me in particular because other language do have error codes that require lookups as I mentioned above and are workable UX.

You are right, of course. I’ve been using Visual Studio for years, and I’ve made do with their numeric warnings and errors. But I can’t say I ever liked it.

Don’t get me wrong: I’d be perfectly happy if the first version of this feature only came with a small handful of string identifiers, to be added to later. And if that’s too much effort, I’d gladly accept a first version with numeric identifiers only. I’m just saying that if I had the choice, I’d prefer string identifiers.

More than one year our team using fork with my pull request https://github.com/microsoft/TypeScript/pull/21602 but with additional comment describing what error is ignored here, like this: //@ts-ignore TS2349 // Cannot invoke an expression whose type lacks a call signature Using it without comment is really not good when you want to understood what is going on here half a year ago.

@glen-84 Already implemented pertty like your suggestion in PR https://github.com/Microsoft/TypeScript/pull/21602 They dont`l like to use codes for this purpose. For me, I have forked typescript and just totally remove some errors I like to ignore.

I would also love this, more specifically for the noUnusedLocals rule, we get errors on this:

class NumberEvaluator {
   private expression;
   ...
   public evaluate(){
      this[this.expression.type]();
   }

   private BinaryExpression() {
      ...
   }
   ...
}

Saying that BinaryExpression is not used, but it might be, depeding on the value of this.expression.type. I could make BinaryExpresison public, but I really think it is a private method.

@wscourge Have a look at the PR #21602 and SR #19139

@wscourge I clearly remember that they argued with something like that the TS error codes are assigned to their message text, not the actual technical error. I.e., if they change the text of one error, they are gonna assign a new TS-number to that, leaving the previous number unused. However silly this sounds, this, according to them, ensures some kind of googling consistency, and hence, the numbers aren’t meaningful representation of the errors, hence, they don’t want to encourage us to rely on it.

@ehoops-cz, I would also like to see this feature added, but I don’t expect it any time soon. In the meantime, you should use a ratchet

Exactly as described above, using @ts-ignore generally considered harmful to the codebase, where you have no idea whether the suppressed line errored because of some unnoticed updates

Jonas, what you want is a “type expectation”. There are a number of libraries that provide an expectType function or a comment-based directive to generate an error when the type of the passed/decorated variable is not what you wanted. Look at

https://github.com/microsoft/dtslint https://github.com/SamVerschueren/tsd https://www.npmjs.com/package/eslint-plugin-expect-type?activeTab=readme

etc

@denissabramovs That’s not very helpful. Check out the thread starting here for some rationale

@thw0rted I believe what you’re describing is what I proposed here, though I confess I haven’t followed this thread super closely of late. I think it addresses @RyanCavanaugh’s upgrade issue

Has anybody proposed solving Ryan’s problem by maintaining a mapping of “deprecated” / “refined” / “OBE” error codes? (Remember folks, naming things is one of the Hard Problems. Sorry.)

In his example, before TS7632 existed, the code would have been marked with TS2339. So, capture TS2339 -> TS7632 somewhere, once, and in future if there’s code with @ts-ignore TS2339 and the compiler spits out TS7632, it walks up the tree and finds an equivalence, so the line is still ignored. There might be cases where this doesn’t work out exactly, but it could at least address some of the concern.

@RyanCavanaugh > there’s a 1:1 relationship between error codes and messages.

It seems to me like this is the source of our problems. Is this something that can be changed?

@RyanCavanaugh Then why not close this issue?

My apologies, I wasn’t trying to get support for my issue. My aim was to share support for this issue being worked on/resolved.

For my issue I could also just do this:

interface LooseObject {
  [key: string]: any
}

document.querySelectorAll("a[href^='#']").forEach(anchor => {
  anchor.addEventListener("click", (event: LooseObject) => {
    // do stuff
  });
});

I prefer Sublime Text but I’m sure search-engine visitors will find these tips helpful.

Wibby, your error isn’t a good use case for ts-ignore.

The issue tracker here isn’t the right place for user support, but the short version is that Event is probably not resolving to the type you think it did. The Hulk-Smash approach is to anycast the variable, like (<any>event).target, but better would be to find out what type TS is actually using for event and fix your type resolution.

If you use an editor that connects to the TS language service (VS Code is very popular) you can just find the line where event is declared and use the “go to definition” command on its type to find the one that’s being used. (If you’re not explicitly typing event then that’s a good place to start.)

I just had a compilation issue in my code where TypeScript incorrectly declared that a property doesn’t exist.

ui/controllers/homepage-controller.ts:22:37 - error TS2339: Property 'target' does not exist on type 'Event'.

22         const elementIdName = event.target.href.split("#").pop();

The lines below helped but it’d be nice if I could just disable a specific thing instead of the entire next line.

// @ts-ignore: TS2339, TypeScript is wrong about `href` not existing on `event`
const elementIdName = event.target.href.split("#").pop();

The ability to ignore particular classes of errors globally (just like you can do in a .eslintrc file) is a must. There is a bit of “squiggly noise” introduced into JS when using checkJS which I would love to disable globally certain issues. checkJS is awesome but that is my only complaint!

in the absence of textual codes such as in python, I find it helpful to allow trailing text in the comment so you can add your own description. you can do this today with @ts-ignore

Recently switched from tslint to eslint and I really like the command line option enabling reporting of useless ignore comments as errors (when the given original error to suppress has not occured). I think it is a pretty good solution to no longer used or changed error codes.

@bmeck I personally have been using numeric error numbers most of my professional career in C and C++ and would not mind them too much. I more recently really started to appreciate the semantic error codes as they are used in eslint and would probably try to use something similar. Even if the naming of 1000+ error codes might not always be perfect, I see a major advantage in hopefully having a semantic structure based on the different types of errors. A little off topic but what is absolutely amazing in eslint is how the hierarchical configuration files are handled and I would really hope for something similar in TypeScript and not only the option to ignore specific error codes.

Ah, I vaguely remember this now.

Doesn’t seem mutually-exclusive though – they could support both. Codes (for now), and later strings as well.

Who’s gonna take the time to pick appropriate names for ~1200 error codes?* 🙂

* Assuming it’s here.

It should be fun for errors like:

An unary expression with the ‘{0}’ operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses.

(off topic: pretty sure that should be a unary, not an unary)

The short string code will probably fail to convey enough information, requiring a documentation lookup anyway.

I think a good compromise would be to support numeric codes for all errors, and short strings for simple/common errors like unused-parameter.

@wosevision Webworker have a special API which is covered by lib.webworker.d.ts After adding the target Webworker to your tsconfig you should be able to have a fully typed code like this: setInterval((this: BroadcastChannel) => this.postMessage('from worker!'), 2000);

disclaimer: I never worked with Webworker till now. For further question opening a stackoverflow question is probably the best.

Here’s another little doozy that I don’t believe is covered by any “normal” TS methods. Consider the following:

You’re dynamically creating a web worker, i.e. by using an ObjectURL created from a Blob containing the .toString()-ified contents of a function:

const objectUrl = URL.createObjectURL(
  new Blob(
    [`(${
      function() { ... }.toString()
    })()`],
    { type: 'application/javascript' }
  )
);

…and the body of that function contains the single most important thing a web worker can do, postMessage():

// inside worker function
setInterval(() => postMessage('from worker!'), 2000);
                              // ^^^ this causes TS error! 😿

Oh no! TypeScript’s implementation of postMessage has a required target parameter in it’s function signature. Problem is, we’re not using window.postMessage, we’re using **self**.postMessage, i.e. the web worker’s context! Furthermore, trying to spoof it with a null value makes it worse:

// inside worker function
setInterval(() => postMessage('from worker!', null), 2000);
                              // ^^^ this causes worker exception! ☠️

The web worker postMessage won’t accept another parameter! Woe is me.

Since it doesn’t make sense to change the [correct] type definition for the window context’s interface, and web workers can’t use TypeScript [natively], it seems this would be a perfect opportunity to tell the TS compiler “Hey dude, that’s a stringified version of some arbitrary Javascript that has nothing to do with you, your execution context even. Back off, get your own sandwich”.

@stweedie I made pull request with ability to specify error code to ignore, https://github.com/Microsoft/TypeScript/pull/21602 but nothing happens, @andy-ms What you think about?