quick-lint-js

Find bugs in JavaScript programs.

TypeScript Clearly & Quickly

quick-lint-js is a JavaScript bug finder. Today, version 3.0 makes it also a TypeScript bug finder! quick-lint-js complements TypeScript with beginner-friendly messages and fast linting.

Example code used in this article has been adapted from various open source projects, including some Copyright Tiny Technologies, Inc. and Copyright Elasticsearch B.V.

Beginner friendly

My goal in developing quick-lint-js is to make JavaScript (and TypeScript) easier for beginners to learn by catching their silly mistakes.

TypeScript's compiler already catches coding mistakes for you. Its fantastic Visual Studio Code extension is the main reason behind its popularity. However, TypeScript is not easy. It has over 80 keywords and many archaic features for compatibility with Node.js, browser, and JavaScript.

What can quick-lint-js do that TypeScript's extension and compiler can't? Let's walk through some examples.

Keyword confusion

I am dyslexic, so sometimes simple issues are hard for me to notice. Can you tell what's wrong with the following code by looking at TypeScript's error messages? Hint: I often confuse different keywords which start with the same letter.

TypeScript
it('matches on first word', await () => {
  await table.searchForItem('Hello');
  await table.expectItemsCount('vis', 1);
});

Expression expected. (1109)
',' expected. (1005)
':' expected. (1005)
',' expected. (1005)
':' expected. (1005)
',' expected. (1005)

quick-lint-js
it('matches on first word', await () => {
  await table.searchForItem('Hello');
  await table.expectItemsCount('vis', 1);
});

'await' cannot be followed by an arrow function; use 'async' instead [E0178]

TSX is not a TypeScript superset

TSX is TypeScript with JSX (React) extensions. JSX is a backwards-compatible extension to JavaScript, so naturally TSX is a backwards-compatible extension to TypeScript, right?

Unfortunately, some valid TypeScript code is not valid TSX. If you copy-paste code from a .ts file to a .tsx file, you might be in for a surprise:

TypeScript
const trim = <T>(f: (a: T) => boolean) => {
  return (arg1: any, arg2: T) => {
    return f(arg2);
  };
};

Cannot find name 'T'. (2304)
JSX element 'T' has no corresponding closing tag. (17008)
Unexpected token. Did you mean `{'>'}` or `&gt;`? (1382)
Unexpected token. Did you mean `{'>'}` or `&gt;`? (1382)
Expression expected. (1109)
Unexpected token. Did you mean `{'>'}` or `&gt;`? (1382)
Expression expected. (1109)
Unexpected token. Did you mean `{'}'}` or `&rbrace;`? (1381)
Unexpected token. Did you mean `{'}'}` or `&rbrace;`? (1381)
'</' expected. (1005)

quick-lint-js
const trim = <T>(f: (a: T) => boolean) => {
  return (arg1: any, arg2: T) => {
    return f(arg2);
  };
};

generic arrow function needs ',' here in TSX [E0285]

JSX is not HTML

In HTML, whitespace is mostly unimportant, and elements can be placed side-by-side naturally. JSX mimics HTML but is its own language with its own quirks, including JSX fragments and JavaScript's famous ASI.

When you make a mistake with JSX, TypeScript does tell you about it, but a beginner might not know how to fix the problem. quick-lint-js gives you better hints so you can quickly fix your code:

TypeScript
export function Spacer() {
  return
    <EuiSpacer size="xs" />
    <EuiHorizontalRule margin="none" />
    <EuiSpacer size="xs" />
}

Unreachable code detected. (7027)
JSX expressions must have one parent element. (2657)

quick-lint-js
export function Spacer() {
  return
    <EuiSpacer size="xs" />
    <EuiHorizontalRule margin="none" />
    <EuiSpacer size="xs" />
}

return statement returns nothing (undefined) [E0179]
missing '<>' and '</>' to enclose multiple children [E0189]

Emoji...⁉️

Since its iPhone debut in 2008, emoji has been the primary method of communication among zoomers. What better place than TypeScript variable names for our favorite emotive icons? Sadly, the designers of TypeScript (and JavaScript) don't let us have fun. 🙁

TypeScript
let 💩 = 'TypeScript';

Cannot find name 'let'. (2304)
Variable declaration not allowed at this location. (1440)
Invalid character. (1127)
Declaration or statement expected. (1128)
Declaration or statement expected. (1128)

quick-lint-js
let 💩 = 'TypeScript';

character is not allowed in identifiers [E0011]

Excited to try quick-lint-js? Installation couldn't be simpler! No package.json nonsense or config file headaches. Just plug quick-lint-js into your editor and it works:

Fast linting

quick-lint-js's name would be nonsense if it wasn't quick. How quick is quick? Beyond human perception.

Quick install

quick-lint-js's VS Code extension installs in 0.48 seconds
quick-lint-js installs in half a second

Installing quick-lint-js is fast and easy. Once it's installed, it lints immediately. No configuration required!

quick-lint-js supports per-project configuration, but you rarely ever need it. Instead, spend more time coding and less time configuring by taking advantage of quick-lint-js's sensible defaults.

Quick lint

You don't need to install quick-lint-js often. What really matters is day-to-day usage. How fast is quick-lint-js at checking code as you type? See for yourself:

quick-lint-js: 0.04 seconds; ESLint: 1.23 seconds; TypeScript: 1.23 seconds
Receive feedback 30× faster with quick-lint-js compared to ESLint and TypeScript in VS Code.

Even on my fast computer, TypeScript and ESLint make coding feel sluggish. But with quick-lint-js, everything is snappy and responsive. (Relatively snappy and responsive, that is. Visual Studio Code is has its own layer of slowness. quick-lint-js feels even better in Vim.)

What's next?

What is in quick-lint-js's future for version 4.0? I am going to play with Vue in 2024, so implementing Vue SFC in quick-lint-js (and also Svelte and plain HTML) sounds like the perfect next step. While focusing on TypeScript, I accumulated a big backlog of tiny bugs, and also thought of some easy-to-implement lint rules.

quick-lint-js version 3.0 has over 3000 patches since version 2.0. Over 70 people made quick-lint-js possible.

Press release written by strager, lead developer of quick-lint-js.