Skip to content

TypeScript

This page curates a list of example ast-grep rules to check and to rewrite TypeScript applications.

TypeScript and TSX are different.

TypeScript is a typed JavaScript extension and TSX is a further extension that allows JSX elements. They need different parsers because of conflicting syntax.

TS allows both the as operator and angle brackets (<>) for type assertions. While TSX only allows the as operator because it interprets angle brackets as JSX elements.

Unnecessary useState Type Has Fix

Description

React's useState is a Hook that lets you add a state variable to your component. The type annotation of useState's generic type argument, for example useState<number>(123), is unnecessary if TypeScript can infer the type of the state variable from the initial value.

We can usually skip annotating if the generic type argument is a single primitive type like number, string or boolean.

Pattern

bash
sg -p 'useState<number>($A)' -r 'useState($A)' -l ts
bash
sg -p 'useState<string>($A)' -r 'useState($A)'
bash
sg -p 'useState<boolean>($A)' -r 'useState($A)'

Example

ts
function Component() {
  const [name, setName] = useState<string>('React')
}

Diff

ts
function Component() {
  const [name, setName] = useState<string>('React') 
  const [name, setName] = useState('React') 
}

Contributed by

Herrington Darkholme

No await in Promise.all array Has Fix

Description

Using await inside an inline Promise.all array is usually a mistake, as it defeats the purpose of running the promises in parallel. Instead, the promises should be created without await and passed to Promise.all, which can then be awaited.

YAML

yaml
id: no-await-in-promise-all
language: typescript
rule:
  pattern: await $A
  inside:
    pattern: Promise.all($)
    stopBy:
      not: { any: [{kind: array}, {kind: arguments}] }
fix: $A

Example

ts
const [foo, bar] = await Promise.all([
  await getFoo(),
  getBar(),
  (async () => { await getBaz()})(),
])

Diff

ts
const [foo, bar] = await Promise.all([
  await getFoo(), 
  getFoo(), 
  getBar(),
  (async () => { await getBaz()})(),
])

Contributed by

Inspired by Alvar Lagerlöf

No console except in catch block Has Fix

Description

Using console methods is usually for debugging purposes and therefore not suitable to ship to the client. console can expose sensitive information, clutter the output, or affect the performance.

The only exception is using console.error to log errors in the catch block, which can be useful for debugging production.

YAML

yaml
id: no-console-except-error
language: typescript
rule:
  any:
    - pattern: console.error($$$)
      not:
        inside:
          kind: catch_clause
          stopBy: end
    - pattern: console.$METHOD($$$)
constraints:
  METHOD:
    regex: 'log|debug|warn'

Example

ts
console.debug('')
try {
    console.log('hello')
} catch (e) {
    console.error(e) // OK
}

Diff

ts
console.debug('') 
try {
    console.log('hello') 
} catch (e) {
    console.error(e) // OK
}

Contributed by

Inspired by Jerry Mouse

Find Import File without Extension

Description

In ECMAScript modules (ESM), the module specifier must include the file extension, such as .js or .mjs, when importing local or absolute modules. This is because ESM does not perform any automatic file extension resolution, unlike CommonJS modules tools such as Webpack or Babel. This behavior matches how import behaves in browser environments, and is specified by the ESM module spec.

The rule finds all imports (static and dynamic) for files without a file extension.

YAML

yaml
id: find-import-file
language: js
rule:
  regex: "/[^.]+[^/]$"
  kind: string_fragment
  any:
    - inside:
        stopBy: end
        kind: import_statement
    - inside:
        stopBy: end
        kind: call_expression
        has:
          field: function
          regex: "^import$"

Example

ts
import a, {b, c, d} from "./file";
import e from "./other_file.js";
import "./folder/";
import {x} from "package";
import {y} from "package/with/path";

import("./dynamic1");
import("./dynamic2.js");

my_func("./unrelated_path_string")

Contributed by

DasSurma in this tweet.

Made with ❤️ with Rust