Question

Suppress TypeScript Errors for One Line: TSLint vs TypeScript

typescriptjavascriptjquerysummernote

Question

A jQuery plugin named Summernote adds a summernote property to the jQuery object. I do not have type definitions for this plugin, but I need to modify its configuration. TypeScript reports the following error:

Property summernote does not exist on type jQueryStatic.

Why do /* tslint:disable */ and /* tslint:enable */ not suppress this error, and how can I handle this error for only these lines?

(function ($) {
  /* tslint:disable */
  delete $.summernote.options.keyMap.pc.TAB;
  delete $.summernote.options.keyMap.mac.TAB;
  /* tslint:enable */
})(jQuery);

The project uses TypeScript with noImplicitAny: true.

Short Answer

You will learn the difference between a TSLint rule violation and a TypeScript compiler type error. You will also learn when to use @ts-ignore, a local any cast, or a proper declaration to work with JavaScript libraries that attach properties dynamically.

Concept

TSLint and TypeScript are separate tools.

  • TSLint checks style and configurable lint rules, such as naming conventions or quote style.
  • TypeScript checks whether code is type-safe and whether properties exist on known types.

This error is produced by the TypeScript compiler, not by TSLint:

Property 'summernote' does not exist on type 'jQueryStatic'.

Therefore, this comment only affects TSLint and cannot hide the compiler error:

/* tslint:disable */

TypeScript knows that $ is a jQueryStatic object. Its installed jQuery type declarations do not say that jQueryStatic has a summernote property, even though the Summernote plugin adds that property when the application runs.

The best long-term fix is to describe the plugin with a type declaration. When that is not practical, keep the unsafe part small by casting only the plugin access to any.

Mental Model

Think of TypeScript as a building inspector and TSLint as a writing-style editor.

  • The style editor can ignore a formatting rule when you add a TSLint disable comment.
  • The building inspector still rejects a plan that references a room that is not on the blueprint.

Summernote creates a real room at runtime, but the jQuery TypeScript blueprint does not include it. You can either update the blueprint with a declaration or temporarily tell TypeScript, “I know this particular value has an unknown shape.”

Take Quiz

Syntax and Examples

A TSLint suppression affects only TSLint:

// tslint:disable-next-line:no-console
console.log('debug message');

A TypeScript suppression affects the next line only:

// @ts-ignore
const pluginOptions = $.summernote.options;

For this situation, a local cast is usually clearer than ignoring an entire line:

(function ($) {
  const summernote = ($ as any).summernote;

  delete summernote.options.keyMap.pc.TAB;
  delete summernote.options.keyMap.mac.TAB;
})(jQuery);

$ as any tells TypeScript not to type-check the summernote property lookup. The rest of the program remains type-checked.

If you use modern TypeScript and expect an error to remain intentionally, prefer @ts-expect-error:

// @ts-expect-error Summernote is loaded globally and has no installed type declaration.
const summernote = $.summernote;

Unlike @ts-ignore, @ts-expect-error reports an error if the next line stops producing a TypeScript error. This helps reveal obsolete suppressions.

Step by Step Execution

Consider this code:

(function ($) {
  const summernote = ($ as any).summernote;
  delete summernote.options.keyMap.pc.TAB;
  delete summernote.options.keyMap.mac.TAB;
})(jQuery);

Step by step:

  1. jQuery is passed into the immediately invoked function and becomes the local $ parameter.
  2. TypeScript considers $ a jQueryStatic value.
  3. ($ as any) creates a type-checking escape hatch for this expression only.
  4. Reading .summernote is allowed because values of type any permit arbitrary property access.
  5. The result is stored in summernote and is also treated as any.
  6. Both delete operations run normally in JavaScript and remove the TAB properties if that object path exists at runtime.

The cast does not change the generated JavaScript. It only changes what TypeScript permits during compilation.

Real World Use Cases

This pattern appears whenever a JavaScript library changes an object dynamically but its TypeScript declarations do not describe that change.

  • Legacy jQuery plugins: A plugin adds $.pluginName or $('#id').pluginName().
  • Browser globals: A script loaded from a CDN adds window.analytics or window.SomeSDK.
  • Application configuration: A server injects a configuration object onto window.
  • Third-party integrations: An older payment, charting, or editor library has incomplete types.
  • Gradual migration: A JavaScript codebase is being moved to TypeScript one file at a time.

In each case, start with a narrow cast or temporary suppression, then add types when the integration becomes important or widely used.

Take Quiz

Real Codebase Usage

In production code, developers usually choose one of these patterns.

1. Narrow local cast for a small one-off use

const summernote = ($ as any).summernote;
delete summernote.options.keyMap.pc.TAB;

This is appropriate when the unknown API is used in one small location.

2. Runtime guard before accessing optional globals

const summernote = ($ as any).summernote;

if (summernote?.options?.keyMap) {
  delete summernote.options.keyMap.pc?.TAB;
  delete summernote.options.keyMap.mac?.TAB;
}

A type cast only satisfies the compiler; it does not guarantee that the plugin was loaded. A guard protects against runtime failures when load order is uncertain.

3. Add a minimal declaration for repeated usage

declare global {
  interface JQueryStatic {
    summernote: any;
  }
}

export {};

Put this in a project declaration file such as src/types/summernote.d.ts. Now $.summernote is accepted throughout the project. You can replace any with a real interface later.

4. Install or write proper type definitions

If reliable type declarations are available for a library, use them. This provides autocomplete, catches misspellings, and documents the integration. Avoid declaring a library as any across a whole application when only one property is missing.

Common Mistakes

Expecting tslint:disable to hide compiler errors

This does not work for TypeScript errors:

/* tslint:disable */
delete $.summernote.options.keyMap.pc.TAB;
/* tslint:enable */

Avoid it: Use @ts-ignore or @ts-expect-error for a one-line compiler suppression, or use a local cast/declaration.

Casting the entire jQuery object unnecessarily

const unsafeJQuery = $ as any;
unsafeJQuery.summernote.options.keyMap.pc.TAB;

This works, but it makes every later use of unsafeJQuery untyped.

Prefer: Cast only where the missing property is accessed.

const summernote = ($ as any).summernote;

Using @ts-ignore without documenting why

// @ts-ignore
delete $.summernote.options.keyMap.pc.TAB;

This hides every TypeScript error on the next line, including unrelated future errors.

Prefer: Use a narrow cast, or explain the exception.

// @ts-expect-error Summernote is provided by a legacy global plugin.
delete $.summernote.options.keyMap.pc.TAB;

Forgetting runtime safety

const summernote = ($ as any).summernote;
delete summernote.options.keyMap.pc.TAB;

This can still throw if Summernote has not loaded.

Avoid it: Ensure the plugin loads first, or check that the expected objects exist before modifying them.

Assuming noImplicitAny creates this error

noImplicitAny reports places where TypeScript would infer any implicitly. The summernote error instead occurs because the known JQueryStatic type does not declare that property.

Comparisons

ApproachScopeBest useTrade-off
/* tslint:disable */TSLint rulesStyle or lint-rule exceptionsDoes not suppress TypeScript compiler errors
// @ts-ignoreNext TypeScript lineShort-lived exceptionCan hide any error on that line
// @ts-expect-errorNext TypeScript lineExpected, documented type errorRequires TypeScript 3.9 or later
($ as any).summernoteOne expressionSmall untyped integrationLoses checking after the cast
interface JQueryStatic { summernote: ... }Project-widePlugin used in several placesRequires maintaining a declaration
Official type definitionsLibrary-wideStable, commonly used APIMay not exist or may be incomplete

A local cast is often the practical middle ground: safer than making all jQuery usage any, but faster than fully modeling a legacy plugin.

Take Quiz

Cheat Sheet

// TSLint only: does NOT affect TypeScript compiler errors
// tslint:disable-next-line:rule-name

// Suppress the next TypeScript error
// @ts-ignore
someCodeThatFailsTypeChecking();

// Prefer when the error is intentional and expected
// @ts-expect-error Explanation of the known missing type
someCodeThatFailsTypeChecking();

// Narrow escape hatch for an undeclared plugin property
const plugin = ($ as any).summernote;

// Declare a property added to jQuery by a plugin
interface JQueryStatic {
  summernote: any;
}

Rules of thumb:

  • A TSLint comment cannot suppress a TypeScript type error.
  • Keep any casts as narrow and local as possible.
  • Use @ts-expect-error rather than @ts-ignore when available.
  • Add a declaration when the untyped API is used repeatedly.
  • Type assertions do not perform runtime checks; verify plugins are loaded when necessary.

FAQ

Why does tslint:disable not suppress the summernote error?

Because the error is generated by the TypeScript compiler. TSLint comments only control TSLint rules.

How do I disable a TypeScript error for one line?

Place // @ts-ignore or, preferably, // @ts-expect-error directly above the line that produces the error.

Is @ts-ignore safe to use?

It is sometimes necessary, but it hides all TypeScript errors on the next line. Prefer a narrow cast or @ts-expect-error with a reason.

What does ($ as any) do in TypeScript?

It tells TypeScript to treat that expression as untyped. It does not convert or validate the value at runtime.

How can I type a jQuery plugin that adds $.summernote?

Use declaration merging to add summernote to JQueryStatic, ideally with a specific interface describing the plugin API.

Does allowJs: true make undeclared plugin properties valid?

No. It allows JavaScript files to be part of the TypeScript project, but it does not add missing properties to TypeScript type declarations.

Should I add summernote: any to JQueryStatic?

It is acceptable as a temporary or small-project solution. For frequent use, define the relevant options and methods with real types.

Take Quiz

Mini Project

Description

Create a small TypeScript setup helper that customizes a globally loaded Summernote plugin without turning the entire jQuery object into any. The helper also verifies that the plugin exists before changing its key map.

Goal

Write a function that removes Summernote's TAB key mapping for both PC and Mac only when the plugin configuration is available.

Requirements

  • Create a function that accepts a JQueryStatic value.
  • Access the undeclared summernote property with a narrow type escape hatch.
  • Do not throw if Summernote or its key map is unavailable.
  • Remove TAB from both the PC and Mac key maps when they exist.
  • Call the function with jQuery.
Take Quiz

Keep learning

Related questions

View more