Emacs has two common LSP clients. Eglot has been part of Emacs since version 29, and its default server list runs typescript-language-server --stdio for js-mode, js-ts-mode and the TypeScript modes. lsp-mode is a separate package whose ts-ls client runs the same server. Either way TypeScript's own server checks the file, with the nearest jsconfig.json, so the errors are tsc's errors.
Install the server with npm (npm install -g typescript-language-server typescript, per its README), then start the client in JavaScript buffers.
At a glance
| Editor | GNU Emacs |
|---|---|
| What to install | Eglot (built in) or lsp-mode |
| What checks the code | Eglot (built in since Emacs 29) or lsp-mode, each running typescript-language-server |
| Where its TypeScript comes from | Decided by typescript-language-server; with lsp-mode, lsp-clients-typescript-prefer-use-project-ts-server asks for the project's copy |
| Reads | jsconfig.json, tsconfig.json |
| How this page was checked | Not run in Emacs for this page. The configuration is from Eglot's source and README and lsp-mode's TypeScript page linked below; what the server reports comes from TypeScript's language service, which the site's tests run. |
Eglot
M-x eglot in a JavaScript buffer starts the server Eglot guesses for the mode. To start it automatically, add eglot-ensure to the mode hooks. Eglot passes the server's diagnostics to Flymake, which underlines them in the buffer.
;; Start Eglot (built in since Emacs 29) in JavaScript buffers
(add-hook 'js-mode-hook #'eglot-ensure)
(add-hook 'js-ts-mode-hook #'eglot-ensure) lsp-mode, and the project's TypeScript
With lsp-mode, M-x lsp-install-server RET ts-ls installs the server. Per its documentation lsp-mode does not prefer the project's tsserver by default; set lsp-clients-typescript-prefer-use-project-ts-server so the editor runs the same TypeScript version as npm run lint.
;; lsp-mode: use node_modules/typescript, the version npm run lint uses
(setq lsp-clients-typescript-prefer-use-project-ts-server t)
(add-hook 'js-mode-hook #'lsp-deferred) What differs from tsc
Diagnostics are reported for the buffers the server has open. Eglot also shows the diagnostic under point through ElDoc. TypeScript's suggestions (unused names with noUnusedLocals off, @deprecated names) come through as well; tsc never prints them.
Make sure it matches tsc
M-x compile with npx tsc -p jsconfig.json --pretty false runs the same check as CI in a compilation buffer. Open a file it reports and compare the codes with what Flymake shows.
Gotchas
- When Eglot manages a buffer it turns off the other Flymake backends, per its README;
eglot-stay-out-ofchanges that if you also run ESLint through Flymake. - Recent development versions of Eglot try another program before
typescript-language-serverfor these modes;eglot-server-programsshows what your version uses.
Sources
- Eglot README · Eglot on GitHub
- eglot.el (eglot-server-programs, eglot-ensure) · Emacs mirror on GitHub
- lsp-mode: JavaScript/TypeScript (ts-ls) · lsp-mode documentation
- typescript-language-server · typescript-language-server on GitHub
Last reviewed 20 October 2026.