VS Code Configuration for TypeScript Development
For .NET engineers who know: Visual Studio 2022 — the IDE with integrated compiler, debugger, NuGet, test runner, designer, and everything else You’ll learn: The mental model shift from “full IDE” to “editor with extensions,” and how to configure VS Code to match Visual Studio’s productivity for TypeScript development Time: 10-15 min read
The .NET Way (What You Already Know)
Visual Studio is a full IDE. It has a built-in C# compiler (Roslyn), a code formatter, a debugger with process attach, a test runner with visual results, a NuGet UI, a project designer, and built-in Git integration. When you install Visual Studio, you get a coherent, co-designed set of tools maintained by Microsoft. The behavior of IntelliSense, the debugger, and the test runner are part of the product.
Extensions in Visual Studio exist but are secondary. The core experience works out of the box.
VS Code is not this. VS Code is a text editor with a powerful extension API. Without extensions, it is a fast, cross-platform text editor with good syntax highlighting and multi-cursor editing. With the right extensions, it becomes a productive TypeScript development environment. The TypeScript language server, the ESLint integration, the debugger, the test runner — each of these is an extension that must be installed and configured.
This distinction matters because: the configuration is your responsibility, it should be committed to the repository so the team shares it, and the experience is composable rather than fixed.
The VS Code Way
Essential Extensions
Install these extensions. The extension IDs are the exact identifiers to use with the Extensions panel or code --install-extension.
Core TypeScript Development
| Extension | ID | .NET Equivalent |
|---|---|---|
| ESLint | dbaeumer.vscode-eslint | Roslyn analyzer red squiggles |
| Prettier - Code Formatter | esbenp.prettier-vscode | Visual Studio format-on-save |
| TypeScript (built-in) | (built-in) | Roslyn IntelliSense |
| Error Lens | usernamehw.errorlens | Inline error display in Visual Studio |
| Path IntelliSense | christian-kohler.path-intellisense | File path autocomplete in string literals |
Git and Code Review
| Extension | ID | .NET Equivalent |
|---|---|---|
| GitLens | eamodio.gitlens | Team Explorer git history + line-level blame |
| GitHub Pull Requests | github.vscode-pull-request-github | Team Explorer PR integration |
AI Assistance
| Extension | ID | .NET Equivalent |
|---|---|---|
| GitHub Copilot | github.copilot | GitHub Copilot for Visual Studio |
| GitHub Copilot Chat | github.copilot-chat | Copilot Chat panel |
Framework-Specific
| Extension | ID | When to Install |
|---|---|---|
| ES7+ React/Redux Snippets | dsznajder.es7-react-js-snippets | React projects |
| Vue - Official | vue.volar | Vue 3 projects |
| Tailwind CSS IntelliSense | bradlc.vscode-tailwindcss | Projects using Tailwind |
| Prisma | prisma.prisma | Any project using Prisma |
Quality of Life
| Extension | ID | What It Does |
|---|---|---|
| Auto Rename Tag | formulahendry.auto-rename-tag | Renames matching HTML/JSX tag |
| Bracket Pair Colorizer | (built-in since VS Code 1.60) | Color-matched brackets |
| Todo Tree | gruntfuturist.todo-tree | Lists TODO comments across project |
Installing Extensions from the CLI
# Install all team-standard extensions at once
code --install-extension dbaeumer.vscode-eslint
code --install-extension esbenp.prettier-vscode
code --install-extension usernamehw.errorlens
code --install-extension eamodio.gitlens
code --install-extension github.copilot
code --install-extension github.copilot-chat
code --install-extension github.vscode-pull-request-github
code --install-extension christian-kohler.path-intellisense
code --install-extension prisma.prisma
code --install-extension dsznajder.es7-react-js-snippets
Or open the Extensions panel (Ctrl+Shift+X / Cmd+Shift+X), search by name, and install.
Workspace Settings — .vscode/settings.json
VS Code settings exist at three levels: user (global), workspace (.vscode/settings.json), and folder (multi-root). Workspace settings are committed to the repository and override user settings for that project. This is how the team shares configuration.
Commit the following .vscode/settings.json to every TypeScript project:
{
// ---- Formatting ----
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.tabSize": 2,
"editor.insertSpaces": true,
"editor.rulers": [100],
"editor.wordWrap": "off",
// ---- TypeScript-specific formatting ----
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[typescriptreact]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[javascript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
// ---- JSON formatting ----
"[json]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"[jsonc]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
// ---- ESLint ----
"eslint.validate": [
"javascript",
"javascriptreact",
"typescript",
"typescriptreact"
],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
// ---- TypeScript ----
"typescript.preferences.importModuleSpecifier": "relative",
"typescript.updateImportsOnFileMove.enabled": "always",
"typescript.suggest.autoImports": true,
"typescript.inlayHints.parameterNames.enabled": "literals",
"typescript.inlayHints.variableTypes.enabled": false,
// ---- Explorer ----
"files.exclude": {
"**/node_modules": true,
"**/.git": true,
"**/dist": true,
"**/.next": true
},
"search.exclude": {
"**/node_modules": true,
"**/dist": true,
"**/.next": true,
"**/pnpm-lock.yaml": true
},
// ---- Editor Experience ----
"editor.minimap.enabled": false,
"editor.bracketPairColorization.enabled": true,
"editor.guides.bracketPairs": "active",
"editor.linkedEditing": true,
"editor.suggest.preview": true
}
What each setting does, mapped to .NET equivalents:
| Setting | VS Code Effect | Visual Studio Equivalent |
|---|---|---|
editor.formatOnSave | Runs Prettier on every save | Format document on save (Tools → Options) |
editor.codeActionsOnSave with ESLint | Runs ESLint auto-fixes on every save | Quick Fix on save (not a VS feature, but similar) |
typescript.updateImportsOnFileMove | Updates imports when you rename/move a file | Visual Studio does this for C# automatically |
editor.linkedEditing | Renames matching HTML/JSX tag when you rename one | Not a Visual Studio feature |
search.exclude | Hides node_modules from search results | Solution Explorer doesn’t show external packages |
Recommended Extensions File — .vscode/extensions.json
The extensions.json file prompts every developer who opens the repository to install the recommended extensions. This eliminates “it works on my machine” due to missing extensions.
{
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"usernamehw.errorlens",
"eamodio.gitlens",
"github.copilot",
"github.copilot-chat",
"christian-kohler.path-intellisense",
"prisma.prisma"
],
"unwantedRecommendations": [
"hookyqr.beautify",
"ms-vscode.vscode-typescript-tslint-plugin"
]
}
unwantedRecommendations suppresses the prompt for extensions that conflict with your setup. beautify conflicts with Prettier. The old tslint plugin is superseded by @typescript-eslint.
When a developer opens the project, VS Code shows a notification: “Do you want to install the recommended extensions?” One click installs everything in the recommendations array.
Debugging Configuration — .vscode/launch.json
The debugger in VS Code is configured via launch.json. This is the equivalent of Visual Studio’s debug profiles (the dropdown next to the Run button).
For a NestJS API:
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug NestJS",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/src/main.ts",
"runtimeArgs": ["--nolazy", "-r", "ts-node/register"],
"sourceMaps": true,
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env",
"console": "integratedTerminal",
"restart": true
},
{
"name": "Attach to Running Process",
"type": "node",
"request": "attach",
"port": 9229,
"sourceMaps": true,
"restart": true
}
]
}
For a Next.js application:
{
"version": "0.2.0",
"configurations": [
{
"name": "Debug Next.js (Server)",
"type": "node",
"request": "launch",
"program": "${workspaceFolder}/node_modules/.bin/next",
"args": ["dev"],
"cwd": "${workspaceFolder}",
"envFile": "${workspaceFolder}/.env.local",
"sourceMaps": true,
"console": "integratedTerminal"
},
{
"name": "Debug Next.js (Client — Chrome)",
"type": "chrome",
"request": "launch",
"url": "http://localhost:3000",
"webRoot": "${workspaceFolder}",
"sourceMaps": true
}
]
}
To debug:
- Set a breakpoint by clicking in the left gutter (same as Visual Studio)
- Press
F5or click the Run and Debug panel (Ctrl+Shift+D) - Select the configuration and press the play button
The experience is close to Visual Studio but requires this upfront configuration. Once launch.json is committed, every team member gets the same debug configurations automatically.
Attaching to a running process works similarly to Visual Studio’s “Attach to Process” (Ctrl+Alt+P):
# Start the dev server with the inspector enabled
node --inspect src/main.js
# Or for pnpm scripts:
NODE_OPTIONS='--inspect' pnpm dev
Then use the “Attach to Running Process” configuration in launch.json.
Task Configuration — .vscode/tasks.json
Tasks in VS Code are runnable actions bound to keyboard shortcuts or the Command Palette. They are equivalent to Visual Studio’s external tools or custom build events.
{
"version": "2.0.0",
"tasks": [
{
"label": "pnpm: dev",
"type": "shell",
"command": "pnpm dev",
"group": "build",
"isBackground": true,
"problemMatcher": [],
"presentation": {
"reveal": "always",
"panel": "dedicated"
}
},
{
"label": "pnpm: test",
"type": "shell",
"command": "pnpm test",
"group": {
"kind": "test",
"isDefault": true
},
"presentation": {
"reveal": "always",
"panel": "shared"
}
},
{
"label": "pnpm: lint",
"type": "shell",
"command": "pnpm lint",
"group": "build",
"presentation": {
"reveal": "always",
"panel": "shared"
}
},
{
"label": "Prisma: migrate dev",
"type": "shell",
"command": "pnpm prisma migrate dev",
"group": "none",
"presentation": {
"reveal": "always",
"panel": "shared"
}
},
{
"label": "Prisma: studio",
"type": "shell",
"command": "pnpm prisma studio",
"group": "none",
"isBackground": true,
"presentation": {
"reveal": "always",
"panel": "dedicated"
}
}
]
}
Run tasks via:
Ctrl+Shift+P→ “Tasks: Run Task” → select the taskCtrl+Shift+Bruns the default build taskCtrl+Shift+T(not the default, but bindable) for the default test task
Multi-Root Workspaces (Monorepos)
If your project is a monorepo with a frontend and backend in separate directories, VS Code supports multi-root workspaces — a single VS Code window with multiple root folders, each with its own settings.
Create a .code-workspace file at the monorepo root:
{
"folders": [
{
"name": "API (NestJS)",
"path": "./apps/api"
},
{
"name": "Web (Next.js)",
"path": "./apps/web"
},
{
"name": "Shared Packages",
"path": "./packages"
}
],
"settings": {
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode"
},
"extensions": {
"recommendations": [
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"prisma.prisma"
]
}
}
Open with: code my-monorepo.code-workspace
Each folder in the workspace can have its own .vscode/settings.json for folder-specific settings (e.g., the API folder might have NestJS-specific snippets enabled, the web folder might have React snippets). The workspace-level settings apply to all folders.
This is the equivalent of opening a Visual Studio Solution that contains multiple projects.
Keyboard Shortcuts
VS Code keyboard shortcuts will feel familiar if you have used Visual Studio, but some differ. The most important for TypeScript development:
| Action | VS Code (Mac) | VS Code (Win/Linux) | Visual Studio Equivalent |
|---|---|---|---|
| Open file by name | Cmd+P | Ctrl+P | Ctrl+, (Go to All) |
| Go to symbol in file | Cmd+Shift+O | Ctrl+Shift+O | Ctrl+F12 |
| Go to symbol in project | Cmd+T | Ctrl+T | Solution-wide search |
| Go to definition | F12 | F12 | F12 |
| Peek definition | Alt+F12 | Alt+F12 | Alt+F12 |
| Find all references | Shift+F12 | Shift+F12 | Shift+F12 |
| Rename symbol | F2 | F2 | F2 |
| Open Command Palette | Cmd+Shift+P | Ctrl+Shift+P | Ctrl+Q (Quick Launch) |
| Toggle terminal | Ctrl+` | Ctrl+` | Alt+F2 (terminal pane) |
| Format document | Shift+Alt+F | Shift+Alt+F | Ctrl+K, Ctrl+D |
| Open Extensions | Cmd+Shift+X | Ctrl+Shift+X | Extensions manager |
| Split editor | Cmd+\ | Ctrl+\ | Drag tab to split |
| Multi-cursor | Alt+Click | Alt+Click | Alt+Click |
| Select all occurrences | Cmd+Shift+L | Ctrl+Shift+L | Ctrl+Shift+H (replace) |
Most valuable shortcut for TypeScript development: Cmd+P (Go to File). Type any part of a filename and navigate there instantly. This replaces browsing Solution Explorer.
Settings Sync
VS Code’s built-in Settings Sync (Cmd+Shift+P → “Settings Sync: Turn On”) syncs your personal settings, extensions, and keybindings across machines via your GitHub or Microsoft account. This covers your global user settings — the team workspace settings come from the repository.
Configure what to sync:
Settings Sync → Settings → What to Sync:
✓ Settings
✓ Keybindings
✓ Extensions
✓ UI State
✗ Profiles (can cause conflicts in team settings)
Team settings go in .vscode/settings.json (committed). Personal preferences go in user settings (synced). Workspace settings always win over user settings for that project.
Key Differences
| Concern | Visual Studio | VS Code |
|---|---|---|
| TypeScript/C# support | Built-in, always present | TypeScript language server (built-in), must configure |
| Formatter | IDE-level setting | Prettier extension + .prettierrc |
| Linter | Roslyn analyzers (built-in) | ESLint extension + eslint.config.mjs |
| Debugger | Visual Studio debugger (full featured) | Node.js debugger via extension (powerful but requires config) |
| Test runner | Test Explorer (visual, built-in) | Vitest extension or terminal |
| NuGet / packages | GUI + PM console | Terminal only (pnpm add) |
| Team settings sharing | .editorconfig (partial) | .vscode/settings.json (comprehensive) |
| Extension model | IDE plugins (heavier) | Extensions (lighter, more composable) |
| Startup speed | Slow (full IDE) | Fast (text editor core) |
| Memory usage | High (200MB-2GB typical) | Lower (100-500MB typical) |
Gotchas for .NET Engineers
Gotcha 1: Format on Save Requires the Right Default Formatter
After installing Prettier, editor.formatOnSave: true alone is not sufficient. VS Code must know which extension to use as the formatter. Without "editor.defaultFormatter": "esbenp.prettier-vscode" (or the per-language equivalent), VS Code may use the built-in TypeScript formatter instead of Prettier.
Symptoms: formatting does not match your .prettierrc config, or VS Code asks “Select a formatter” every time you save.
The fix is in .vscode/settings.json:
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"[typescript]": {
"editor.defaultFormatter": "esbenp.prettier-vscode"
}
}
The language-specific override ([typescript]) takes precedence over the global one. Set both to be safe.
Gotcha 2: ESLint Type-Checked Rules Require tsconfig.json in the Right Place
Some @typescript-eslint rules (including no-floating-promises) require the TypeScript compiler to provide type information. The ESLint extension needs to find your tsconfig.json. If your project has an unusual structure (tsconfig not at the workspace root, or a monorepo with multiple tsconfigs), the ESLint extension may silently skip type-checked rules.
Diagnosis: open the ESLint output panel (View → Output → ESLint) and look for errors referencing tsconfig.json or parserOptions.project.
Fix for monorepos — in each sub-package’s eslint.config.mjs:
{
languageOptions: {
parserOptions: {
project: './tsconfig.json', // Explicit relative path
tsconfigRootDir: __dirname,
},
},
}
Gotcha 3: VS Code’s TypeScript Version vs. Your Project’s TypeScript Version
VS Code bundles its own version of TypeScript for the language server. Your project installs TypeScript as a dev dependency. These versions can differ — VS Code’s bundled version might be older or newer than your project’s.
This causes subtle issues: a feature available in TypeScript 5.5 might not show IntelliSense hints if VS Code’s bundled TypeScript is 5.3.
Fix: tell VS Code to use your project’s TypeScript version:
Cmd+Shift+P → "TypeScript: Select TypeScript Version"
→ "Use Workspace Version"
Or set it in .vscode/settings.json:
{
"typescript.tsdk": "node_modules/typescript/lib"
}
This should be in every project’s .vscode/settings.json. It ensures every developer uses the same TypeScript version as the CI build.
Gotcha 4: Extensions Are Not Enabled in All Workspaces by Default
Some extensions ask whether they should be enabled globally or per-workspace when installed. Others are enabled globally by default. For security-sensitive extensions (like anything that reads your files), you may want per-workspace control.
If an extension is installed but not working in a specific project, check Cmd+Shift+P → “Extensions: Show Installed Extensions” and verify the extension is enabled for the current workspace, not just globally.
For the ESLint extension specifically: it must have a valid eslint.config.mjs (or legacy .eslintrc) in the project to show any errors. If the config file is missing, the extension is silently inactive.
Hands-On Exercise
Set up VS Code from scratch for a TypeScript project using only committed configuration files.
Step 1: Install the extensions
Run the code --install-extension commands from the Essential Extensions section above. After installing, verify each extension appears in the Extensions panel.
Step 2: Create the workspace configuration files
In a TypeScript project you are working on, create:
.vscode/
├── settings.json (from the template above)
├── extensions.json (from the template above)
├── launch.json (from the appropriate template for NestJS or Next.js)
└── tasks.json (from the template above)
Commit these four files. Note that .vscode/ is typically not in .gitignore — it should be committed for team standardization.
Step 3: Verify the configuration
Open a TypeScript file and:
- Introduce a deliberate formatting error (wrong indentation). Save the file. Prettier should fix it.
- Introduce a deliberate ESLint violation (e.g., an unused variable). Verify the red underline appears inline (Error Lens).
- Set a breakpoint in a function and press F5. Verify the debugger stops at the breakpoint.
Step 4: Test the task runner
Ctrl+Shift+P → “Tasks: Run Task” → run pnpm: test. Verify the test output appears in the terminal panel.
Step 5: Share with the team
In the project’s README, add a one-line note that .vscode/ is committed and what it provides. Paste the code --install-extension commands into your CONTRIBUTING.md or project README so new team members can get set up in one step.
Quick Reference
Files to Commit to Every TypeScript Project
| File | Purpose |
|---|---|
.vscode/settings.json | Formatting, ESLint, TypeScript preferences |
.vscode/extensions.json | Extension recommendations |
.vscode/launch.json | Debug configurations |
.vscode/tasks.json | Runnable tasks (build, test, migrate) |
.prettierrc | Prettier formatting rules |
eslint.config.mjs | ESLint rule configuration |
tsconfig.json | TypeScript compiler options |
Essential Settings Quick Reference
{
"editor.formatOnSave": true,
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.codeActionsOnSave": { "source.fixAll.eslint": "explicit" },
"typescript.tsdk": "node_modules/typescript/lib",
"typescript.updateImportsOnFileMove.enabled": "always"
}
Extension ID Reference
| Extension | ID |
|---|---|
| ESLint | dbaeumer.vscode-eslint |
| Prettier | esbenp.prettier-vscode |
| Error Lens | usernamehw.errorlens |
| GitLens | eamodio.gitlens |
| GitHub Copilot | github.copilot |
| GitHub Copilot Chat | github.copilot-chat |
| GitHub Pull Requests | github.vscode-pull-request-github |
| Path IntelliSense | christian-kohler.path-intellisense |
| Prisma | prisma.prisma |
| Vue - Official | vue.volar |
| Tailwind CSS IntelliSense | bradlc.vscode-tailwindcss |
| ES7+ React Snippets | dsznajder.es7-react-js-snippets |
Further Reading
- VS Code Documentation — User and Workspace Settings — Complete settings reference
- VS Code Documentation — Debugging — Full debugger configuration reference, including remote debugging
- VS Code Documentation — Tasks — Task system reference for advanced task configuration
- typescript-eslint in VS Code — Specific guidance for configuring type-aware ESLint in VS Code