Skip to content

TypeScript Style Guide

Rules

Require that member overloads be consecutive

@typescript-eslint/adjacent-overload-signatures

Grouping overloaded members together can improve readability of the code.

// Incorrect
declare namespace Foo {
  export function foo(s: string): void
  export function foo(n: number): void
  export function bar(): void
  export function foo(sn: string | number): void
}
// Correct
declare namespace Foo {
  export function foo(s: string): void
  export function foo(n: number): void
  export function foo(sn: string | number): void
  export function bar(): void
}

Disallow awaiting a value that is not a Thenable

@typescript-eslint/await-thenable

This rule disallows awaiting a value that is not a "Thenable" (an object which has then method, > such as a Promise). While it is valid JavaScript to await a non-Promise-like value (it will > resolve immediately), this pattern is often a programmer error, such as forgetting to add > parenthesis to call a function that returns a Promise.

// Incorrect
await 'value'

const createValue = () => 'value'
await createValue()

// Correct
await Promise.resolve('value')

const createValue = async () => 'value'
await createValue()

Disallow @ts- comments or require descriptions after directive

@typescript-eslint/ban-ts-comment

TypeScript provides several directive comments that can be used to alter how it processes files. Using these to suppress TypeScript Compiler Errors reduces the effectiveness of TypeScript overall.

https://typescript-eslint.io/rules/ban-ts-comment/

Disallow the declaration of empty interfaces

@typescript-eslint/no-empty-interface

// Incorrect
// an empty interface
interface Foo {}

// an interface with only one supertype (Bar === Foo)
interface Bar extends Foo {}

// an interface with an empty list of supertypes
interface Baz {}

// Correct
interface Foo {
  name: string
}

// same as above
interface Bar {
  age: number
}

// an interface with more than one supertype
// in this case the interface can be used as a replacement of a union type.
interface Baz extends Foo, Bar {}

Disallow the any type

'@typescript-eslint/no-explicit-any': [ 'error', { fixToUnknown: false, ignoreRestArgs: true } ]

// Incorrect
const age: any = 'seventeen';
const ages: any[] = ['seventeen'];
function greet(param: Array<any>): Array<any> {}
// Correct
const age: number = 17;
const ages: number[] = [17];
function greet(param: Array<string>): Array<string> {}

Disallow extra non-null assertion

@typescript-eslint/no-extra-non-null-assertion

// Incorrect
const foo: { bar: number } | null = null
const bar = foo!!!.bar
// Correct
const foo: { bar: number } | null = null
const bar = foo!.bar

Disallow iterating over an array with a for-in loop

@typescript-eslint/no-for-in-array

A for-in loop (for (var k in o)) iterates over the properties of an Object. While it is legal to use for-in loops with array types, it is not common. for-in will iterate over the indices of the array as strings, omitting any "holes" in the array. More common is to use for-of, which iterates over the values of an array.

// Incorrect
for (const x in [3, 4, 5]) {
  console.log(x)
}

// Correct
for (const x in { a: 3, b: 4, c: 5 }) {
  console.log(x)
}

Disallow explicit type declarations for variables or parameters initialized to a number, string, or boolean

@typescript-eslint/no-inferrable-types

// Incorrect
const a: bigint = 10n
const a: bigint = -10n
const a: bigint = BigInt(10)
const a: bigint = -BigInt(10)
const a: boolean = false
const a: boolean = true

// Correct
const a = 10n
const a = -10n
const a = BigInt(10)
const a = -BigInt(10)
const a = false
const a = true

Disallow non-null assertions after an optional chain expression

@typescript-eslint/no-non-null-asserted-optional-chain

// Incorrect
foo?.bar!
foo?.bar()!

foo?.bar!.baz
foo?.bar!()
foo?.bar!().baz
// Correct
foo?.bar
(foo?.bar).baz
foo?.bar()
foo?.bar()
foo?.bar().baz

Disallow returning a value with type any from a function

@typescript-eslint/no-unsafe-return

// Incorrect
function foo1() {
  return 1 as any
}
function foo2() {
  return Object.create(null)
}

// Correct
function foo1() {
  return 1
}
function foo2() {
  return Object.create(null) as Record<string, unknown>
}

Disallow require statements except in import statements

@typescript-eslint/no-var-requires

// Incorrect
var foo = require('foo')
const foo = require('foo')
let foo = require('foo')
// Correct
import foo = require('foo')
require('foo')
import foo from 'foo'

Require using namespace keyword over module keyword to declare custom TypeScript modules

@typescript-eslint/prefer-namespace-keyword

In an effort to prevent further confusion between custom TypeScript modules and the new ES2015 modules, starting with TypeScript v1.5 the keyword namespace is now the preferred way to declare custom TypeScript modules

Require both operands of addition to have type number or string

@typescript-eslint/restrict-plus-operands

// Incorrect
var foo = '5.5' + 5
var foo = 1n + 1
// Correct
var foo = parseInt('5.5', 10) + 10
var foo = 1n + 1n

Enforce template literal expressions to be of string type

@typescript-eslint/restrict-template-expressions

// Incorrect
const arg1 = [1, 2]
const msg1 = `arg1 = ${arg1}`

const arg2 = { name: 'Foo' }
const msg2 = `arg2 = ${arg2 || null}`
// Correct
const arg = 'foo'
const msg1 = `arg = ${arg}`
const msg2 = `arg = ${arg || 'default'}`

const stringWithKindProp: string & { _kind?: 'MyString' } = 'foo'
const msg3 = `stringWithKindProp = ${stringWithKindProp}`

Setting in project

If there is .eslintrc.{js,yml,json} in your project, you can add '@vue/eslint-config-typescript' as extension.

extends: ['@vue/eslint-config-typescript']

Then, add some lints in rules.

'@typescript-eslint/adjacent-overload-signatures': 'error',
'@typescript-eslint/ban-ts-comment': [
  'error',
  {
    'ts-expect-error': 'allow-with-description',
    'ts-ignore': true,
    'ts-nocheck': true,
    'ts-check': false,
    minimumDescriptionLength: 3
  }
],
'@typescript-eslint/no-empty-interface': 'error',
'@typescript-eslint/no-explicit-any': [
  'error',
  {
    fixToUnknown: false,
    ignoreRestArgs: true
  }
],
'@typescript-eslint/no-extra-non-null-assertion': 'error',
'@typescript-eslint/no-for-in-array': 'error',
'@typescript-eslint/no-inferrable-types': 'error',
'@typescript-eslint/no-non-null-asserted-optional-chain': 'error',
'@typescript-eslint/no-var-requires': 'error',
'@typescript-eslint/prefer-namespace-keyword': 'error'

References

TypeScript ESLint