Alright, let’s talk about the duct tape and baling wire of your TypeScript migration: @ts-ignore and its slightly more responsible cousin, @ts-expect-error. You’re going to use these. I’ve used them. We all have. They are the temporary barricades you throw up against the tsunami of red squiggles so you can actually compile your code and keep the business running while you methodically fix things. The key word here is temporary. Think of them as a “Check Engine” light you’re actually going to get around to fixing, not one you just put a piece of electrical tape over.

Here’s the deal. You’ve just added TypeScript to a massive, loosely-typed JavaScript codebase. It’s screaming. Functions are getting any, objects are missing properties, and the whole thing looks like a crime scene. You can’t fix it all in one go. This is where these directives come in. They tell the TypeScript compiler, “I see this error, I know about it, and for the love of all that is holy, please shut up about it for now.”

The Blunt Instrument: @ts-ignore

@ts-ignore is the simpler, dumber tool. You plop it on the line before the line that has an error, and the compiler will completely ignore any error on that next line. It doesn’t care what the error is or how severe it is. It just silences it.

// @ts-ignore
const legacyResult = someOldFunctionThatReturnsWhoKnowsWhat();
legacyResult.doSomethingFoolish(); // This error is also silenced!

See the problem? It’s a sledgehammer. It suppresses every error on the very next line. If someOldFunctionThatReturnsWhoKnowsWhat returns undefined, then the next line trying to call .doSomethingFoolish() on undefined will also be silently ignored. Your code will blow up at runtime, and TypeScript, which could have warned you, was told not to. Use this with extreme caution.

The Responsible Sibling: @ts-expect-error

@ts-expect-error is the one you want to use 99% of the time. It’s more precise. It says, “I expect an error to be on this line.” It’s a deliberate assertion.

// @ts-expect-error: We know this function is missing a `status` property.
const user: User = createLegacyUserObject();

Here’s the brilliant part: if you put a @ts-expect-error directive and there isn’t an error on that line, TypeScript will itself throw an error, telling you your suppression is unnecessary.

// You later refactor and fix the function...
function createLegacyUserObject(): User {
  return { name: 'Alice', status: 'active' }; // Now it's correct!
}

// @ts-expect-error // TypeScript will now error HERE, saying:
// "Unused '@ts-expect-error' directive."
const user: User = createLegacyUserObject();

This is your built-in reminder to clean up your suppressions. It turns a forgotten @ts-ignore from a permanent blind spot into a temporary, self-cleaning TODO item. This is a massive win.

Best Practices and Pitfalls

First, always, always add a comment explaining why you’re suppressing the error. Your future self, or the next developer, will thank you. Was it a third-party library type issue? A legacy function that’s too risky to touch right now? A weird union type that’s impractical to fix? Write it down.

// @ts-expect-error: Library types are wrong for this edge case. See ticket DEV-1234.
const data = someLib.getData(options);

Second, be as granular as possible. Don’t slap these directives on a block of code. Isolate the single line causing the problem. The goal is to keep the type checking as tight as possible everywhere else.

Third, treat these suppressions like TODOs. Use your linter or a script to track their number and make it a goal to reduce them over time. A @ts-expect-error is a debt. It’s not inherently bad to take on debt, but you must have a plan to pay it off.

Finally, understand the difference in scope. @ts-ignore suppresses all errors on the next line. @ts-expect-error expects a specific error on its own line. This makes @ts-expect-error far safer and more intentional. The only time I might reach for @ts-ignore is if a single line is generating a dozen errors from deeply nested generic type mismatches that are truly impossible to untangle at the moment, and you just need it to compile. But that should be a rare, well-documented exception.

Use these tools to keep momentum during your migration, but never let them become a permanent fixture. Their entire job is to become obsolete.