Skip to main content
Advertisement

1.5 Developer Tooling

Writing correct code matters, but having good development tools in place dramatically reduces mistakes. TypeScript integrates deeply with IDEs to deliver autocomplete, instant error highlighting, and safe refactoring. This chapter covers everything from optimizing VS Code for TypeScript, to maintaining code quality with ESLint, enforcing consistent style with Prettier, and understanding how to read TypeScript error messages.

VS Code TypeScript Support

TypeScript was created by Microsoft, and so was VS Code. These two tools were designed together from the ground up. VS Code supports TypeScript completely out of the box — no additional plugins required.

The built-in TypeScript server (tsserver)

VS Code runs an internal TypeScript language server called tsserver. Running in the background, tsserver analyzes your code and provides the following features:

  • IntelliSense: Automatically suggests object properties, function parameters, and return types based on type information
  • Instant error display: Highlights errors with red underlines the moment you type code — no need to save the file
  • Hover type information: Shows the type of a variable or function when you hover over it with the mouse
  • Go to Definition: Jump directly to the definition of a function or type with F12 or Ctrl+Click
  • Find All References: Find every location a function or type is used with Shift+F12
  • Rename Symbol: Safely rename variables and functions across the entire project with F2

Switching the TypeScript version in VS Code

The TypeScript version installed locally in your project may differ from the version built into VS Code. It is recommended to configure VS Code to use the project's TypeScript version.

  1. Open any .ts file and press Ctrl+Shift+P (open the Command Palette)
  2. Search for "TypeScript: Select TypeScript Version"
  3. Select "Use Workspace Version"

This setting is saved in .vscode/settings.json.

{
"typescript.tsdk": "node_modules/typescript/lib"
}

Useful VS Code settings (settings.json)

Add project-specific VS Code settings in .vscode/settings.json. Commit this file to Git so that all team members share the same settings.

{
// TypeScript version — use the project's local version
"typescript.tsdk": "node_modules/typescript/lib",

// Enable auto-import suggestions
"typescript.suggest.autoImports": true,

// Automatically update import paths when files are moved
"typescript.updateImportsOnFileMove.enabled": "always",

// Inlay hints — display inferred types directly in the code
"typescript.inlayHints.parameterNames.enabled": "literals",
"typescript.inlayHints.variableTypes.enabled": false,
"typescript.inlayHints.returnTypes.enabled": true,
"typescript.inlayHints.functionLikeReturnTypes.enabled": true,

// Auto-format on save (when using Prettier)
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",

// Auto-fix ESLint issues on save
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
}
}

Inlay hints

Inlay hints display type information as a semi-transparent overlay directly in your code. Even without explicit type annotations, you can see the inferred types right in the editor.

// What you see with inlayHints.parameterNames.enabled: "all"
const result = arr.reduce(/*accumulator:*/ (acc, /*currentValue:*/ cur) => acc + cur, 0);

// What you see with inlayHints.returnTypes.enabled: true
function add(a: number, b: number) /*: number*/ {
return a + b;
}

ESLint + typescript-eslint

The TypeScript compiler catches type errors. ESLint catches everything else: unused variables, unnecessary any types, potentially buggy patterns, and more. In TypeScript projects, ESLint is used together with the typescript-eslint plugin.

Installation

npm install --save-dev eslint @eslint/js typescript-eslint

eslint.config.mjs configuration (ESLint v9 Flat Config)

ESLint v9 and later use the eslint.config.mjs file (the Flat Config format).

// eslint.config.mjs
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';

export default tseslint.config(
// JavaScript recommended rules
eslint.configs.recommended,

// TypeScript recommended rules
...tseslint.configs.recommended,

// Project-specific custom rules
{
rules: {
// Warn on any usage (warning instead of error)
'@typescript-eslint/no-explicit-any': 'warn',

// Error on unused variables
'@typescript-eslint/no-unused-vars': [
'error',
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' }
],

// Allow empty functions
'@typescript-eslint/no-empty-function': 'off',
},
}
);

argsIgnorePattern: '^_' ignores variables whose names start with _ even if they are unused. This is compatible with the convention of marking intentionally unused parameters as _name.

Key rules in @typescript-eslint/recommended

Representative rules included in tseslint.configs.recommended:

RuleDescription
no-explicit-anyDisallow or warn on any type usage
no-unused-varsError on unused variables
no-non-null-assertionDisallow the non-null assertion operator !
prefer-as-constRecommend using as const
ban-typesDisallow wrapper types like Object, String, etc.
no-inferrable-typesDisallow type annotations where types can be inferred

Adding package.json scripts

{
"scripts": {
"lint": "eslint src",
"lint:fix": "eslint src --fix"
}
}
npm run lint       # Print list of errors
npm run lint:fix # Auto-fix errors that can be fixed automatically

Prettier Integration

ESLint handles code quality; Prettier enforces code style — indentation, quotes, semicolons, and so on. With Prettier, every team member writes code in the same format.

Installation

npm install --save-dev prettier eslint-config-prettier
  • prettier: The code formatter
  • eslint-config-prettier: Disables ESLint rules that conflict with Prettier

.prettierrc configuration

Create a .prettierrc file at the project root.

{
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2,
"endOfLine": "lf"
}
OptionDefaultDescription
semitrueAdd semicolons
trailingComma"all"Add trailing comma after the last item
singleQuotefalseUse single quotes
printWidth80Maximum line length
tabWidth2Number of spaces per indent
endOfLine"lf"Line ending character (LF/CRLF)

Integrating ESLint and Prettier

Add eslint-config-prettier to eslint.config.mjs. It must be placed last.

// eslint.config.mjs
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import eslintConfigPrettier from 'eslint-config-prettier';

export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommended,
{
rules: {
'@typescript-eslint/no-explicit-any': 'warn',
},
},
// Added last — disables all ESLint rules that conflict with Prettier
eslintConfigPrettier,
);

Adding formatting scripts to package.json

{
"scripts": {
"format": "prettier --write src",
"format:check": "prettier --check src"
}
}
npm run format        # Auto-format all files
npm run format:check # Check only whether formatting is needed (for CI)

.editorconfig Configuration

.editorconfig establishes a consistent baseline coding style across different editors. VS Code, IntelliJ, Vim, and many other editors all recognize this file. Used together with Prettier, it unifies even editor-level default settings.

Create an .editorconfig file at the project root.

# .editorconfig
root = true

[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

[*.md]
trim_trailing_whitespace = false

To apply .editorconfig in VS Code, install the "EditorConfig for VS Code" extension.

Reading TypeScript Error Messages

TypeScript error messages can feel unfamiliar at first, but once you understand the pattern they become quick to decode.

Error message structure

src/index.ts:10:20 - error TS2345: Argument of type 'string' is not assignable
to parameter of type 'number'.
  • src/index.ts:10:20 — File path, line number (10), column number (20)
  • error — Severity (error / warning)
  • TS2345 — TypeScript error code
  • The rest — A human-readable description

Commonly encountered error codes

TS2322 — Type 'X' is not assignable to type 'Y'

The most frequently seen error. Occurs when you try to assign a value whose type doesn't match.

const age: number = "30";
// TS2322: Type 'string' is not assignable to type 'number'.

TS2345 — Argument of type 'X' is not assignable to parameter of type 'Y'

Occurs when you pass an argument of the wrong type to a function.

function greet(name: string): void { }
greet(42);
// TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.

TS2339 — Property 'X' does not exist on type 'Y'

Occurs when you access a property that doesn't exist on an object.

const user = { name: "Alice" };
console.log(user.age);
// TS2339: Property 'age' does not exist on type '{ name: string; }'.

TS2531 — Object is possibly 'null'

Occurs in strict mode when you access a value that could be null.

const element = document.getElementById("app");
element.textContent = "Hello";
// TS2531: Object is possibly 'null'.

// Fix
element?.textContent = "Hello"; // optional chaining
// or
if (element) element.textContent = "Hello";

TS7006 — Parameter 'X' implicitly has an 'any' type

Occurs in strict mode when a function parameter has no type annotation.

function process(data) {  // no type on data
// TS7006: Parameter 'data' implicitly has an 'any' type.
}

// Fix
function process(data: string) { }

TS2304 — Cannot find name 'X'

Occurs when you use a variable or type that has not been declared.

console.log(undeclaredVariable);
// TS2304: Cannot find name 'undeclaredVariable'.

Practical Example: Full Development Environment Setup Step by Step

This is the complete process for setting up a development environment for a new TypeScript project from scratch.

Step 1: Initialize the project

mkdir my-typescript-app
cd my-typescript-app
npm init -y
mkdir src

Step 2: Install core packages

# TypeScript and execution tools
npm install --save-dev typescript tsx @types/node

# ESLint and TypeScript plugin
npm install --save-dev eslint @eslint/js typescript-eslint

# Prettier and ESLint integration
npm install --save-dev prettier eslint-config-prettier

Step 3: Configure tsconfig.json

npx tsc --init

Edit tsconfig.json to look like this:

{
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"lib": ["ES2022"],
"rootDir": "./src",
"outDir": "./dist",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"sourceMap": true,
"resolveJsonModule": true
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist"]
}

Step 4: Configure ESLint

// eslint.config.mjs
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
import eslintConfigPrettier from 'eslint-config-prettier';

export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommended,
{
rules: {
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': [
'error',
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
],
},
},
eslintConfigPrettier,
);

Step 5: Configure Prettier

// .prettierrc
{
"semi": true,
"trailingComma": "all",
"singleQuote": true,
"printWidth": 100,
"tabWidth": 2,
"endOfLine": "lf"
}

Step 6: Configure VS Code

// .vscode/settings.json
{
"typescript.tsdk": "node_modules/typescript/lib",
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"typescript.inlayHints.parameterNames.enabled": "literals",
"typescript.inlayHints.returnTypes.enabled": true
}

Step 7: Configure .editorconfig

root = true

[*]
charset = utf-8
end_of_line = lf
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true

Step 8: Add .gitignore

node_modules/
dist/
*.js.map
.DS_Store

Step 9: Add package.json scripts

{
"scripts": {
"dev": "tsx watch src/index.ts",
"build": "npm run type-check && tsc",
"start": "node dist/index.js",
"type-check": "tsc --noEmit",
"lint": "eslint src",
"lint:fix": "eslint src --fix",
"format": "prettier --write src",
"format:check": "prettier --check src"
}
}

Step 10: Write the first source file and verify everything works

// src/index.ts
interface User {
id: number;
name: string;
email: string;
}

function createWelcomeMessage(user: User): string {
return `Welcome, ${user.name}! (${user.email})`;
}

const user: User = {
id: 1,
name: 'Developer',
email: 'dev@example.com',
};

console.log(createWelcomeMessage(user));
npm run dev
# Welcome, Developer! (dev@example.com)

npm run type-check
# No type errors

npm run lint
# No lint errors

Pro Tips

// @ts-expect-error vs // @ts-ignore

Both comments suppress TypeScript errors on the next line. However, there is an important difference.

// @ts-ignore — suppresses unconditionally, whether or not an error exists
// @ts-ignore
const x: number = "hello"; // error suppressed

// @ts-expect-error — suppresses only when an error is present
// becomes an error itself if no error exists
// @ts-expect-error
const y: number = "world"; // error suppressed

// What if you add @ts-expect-error but there is no error?
// @ts-expect-error
const z: number = 42; // Error: Unused '@ts-expect-error' directive.

@ts-expect-error is safer. If the code is later fixed so the error disappears, the @ts-expect-error comment itself becomes an error and notifies you. By contrast, @ts-ignore silently stays in place even after the error is gone.

In practice, prefer @ts-expect-error over @ts-ignore. Always leave a comment explaining why the error is being suppressed.

// @ts-expect-error — incorrect type in third-party library definitions, remove after library update
someLibrary.undocumentedMethod();

Exploring built-in types with "Go to Definition"

Ctrl+Click (or F12) lets you jump to the definition of any TypeScript built-in type. You can see exactly how the methods and properties of built-ins like Array, Promise, and Map are defined.

For example, navigating to the type definition of Array.prototype.map reveals:

// lib.es5.d.ts (TypeScript built-in type definitions)
interface Array<T> {
map<U>(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[];
}

Reading this type definition tells you precisely how map uses generics and what the callback's parameters are. It is more accurate than looking things up in library documentation.

Adding a list of recommended extensions to .vscode/extensions.json causes VS Code to prompt team members to install them automatically when they open the project.

// .vscode/extensions.json
{
"recommendations": [
"esbenp.prettier-vscode",
"dbaeumer.vscode-eslint",
"EditorConfig.EditorConfig",
"usernamehw.errorlens"
]
}

Summary

ToolRoleConfig file
VS Code tsserverType checking, autocomplete, error display.vscode/settings.json
ESLint + typescript-eslintCode quality, anti-pattern detectioneslint.config.mjs
PrettierConsistent code style.prettierrc
eslint-config-prettierPrevent ESLint + Prettier conflictsLast entry in eslint.config.mjs
.editorconfigUnify baseline editor settings.editorconfig
Error codeMeaning
TS2322Type mismatch assignment
TS2345Function argument type mismatch
TS2339Accessing a non-existent property
TS2531Possible null/undefined value
TS7006Implicit any type on a parameter
TS2304Using an undeclared name

The next chapter dives into TypeScript's type system in earnest — starting from the primitives string, number, and boolean, and building up to union types, intersection types, and literal types that form the foundation of TypeScript's type model.

Advertisement