Type under cursor in TypeScript file

Viewed 755

I want to investigate certain inferred types in TypeScript code. This would be especially useful in case of correct files to be sure that inferred types are specific enough, exact and correct.

Given TypeScript file how to get type at specific position in it, in similar manner to flow type-at-pos?

I don't need any additional complex tooling beyond this, like IDE integrations. Just this simple functionality.

1 Answers

Unfortunately TypeScript doesn't expose anything like this in an easy-to-consume manner. TypeScript does have a language server that you can use to query for type information. It's relatively easy to do, but is cumbersome. Basically you launch the server and send some commands to open a project and file, then send a command to request info.

If you have typescript installed globally (npm install -g typescript) you should be able to run the typescript server under the tsserver executable. Otherwise, you can run it by locating it inside the bin folder within the typescript module.

Commands are sent from stdin and responses are sent via stdout. The commands are just JSON and terminated with a new line.

The first command you'll want to send is openExternalProject:

{"type":"request","command":"openExternalProject","arguments":{"projectFileName":"/path/to/tsconfig.json","rootFiles":[],"options":{}}}

Replace /path/to/tsconfig.json with the path to your tsconfig.json file.

The next command is to open a file:

{"type":"request","command":"open","arguments":{"file":"/path/to/file.ts"}}

And the final command to get type information is quickinfo:

{"type":"request","command":"quickinfo","arguments":{"offset":9,"line":5,"file":"/path/to/file.ts"}}

Replace the line value with the line of the type you want info on, and the offset to the line offset.

You should get a response from the server similar to this:

{"seq":0,"type":"response","command":"quickinfo","success":true,"body":{"kind":"alias","kindModifiers":"","start":{"line":5,"offset":8},"end":{"line":5,"offset":19},"displayString":"(alias) const MyComponent: new () => MyComponent\nimport MyComponent","documentation":"","tags":[]}}

Here's my complete request/response when I tested this:

> ./tsserver
Content-Length: 76

{"seq":0,"type":"event","event":"typingsInstallerPid","body":{"pid":63491}}
{"type":"request","command":"openExternalProject","arguments":{"projectFileName":"/test/tsconfig.json","rootFiles":[],"options":{}}}
Content-Length: 526

{"seq":0,"type":"event","event":"telemetry","body":{"telemetryEventName":"projectInfo","payload":{"projectId":"489d647358fb01382e6efc991212c7fd2bd8d16a91e6b960aa17b2c86ff9cb25","fileStats":{"js":0,"jsSize":0,"jsx":0,"jsxSize":0,"ts":0,"tsSize":0,"tsx":0,"tsxSize":0,"dts":0,"dtsSize":0,"deferred":0,"deferredSize":0},"compilerOptions":{},"typeAcquisition":{"enable":true,"include":false,"exclude":false},"compileOnSave":true,"configFileName":"other","projectType":"external","languageServiceEnabled":true,"version":"3.7.2"}}}
Content-Length: 87

{"seq":0,"type":"response","command":"openExternalProject","success":true,"body":true}
Content-Length: 415

{"seq":0,"type":"event","event":"setTypings","body":{"projectName":"/test/tsconfig.json","typeAcquisition":{"include":[],"exclude":[],"enable":true},"compilerOptions":{"allowNonTsExtensions":true,"noEmitForJsFiles":true},"typings":["/Users/so-test/Library/Caches/typescript/3.7/node_modules/@types/react/index.d.ts"],"unresolvedImports":[],"kind":"action::set"}}
Content-Length: 415

{"seq":0,"type":"event","event":"setTypings","body":{"projectName":"/test/tsconfig.json","typeAcquisition":{"include":[],"exclude":[],"enable":true},"compilerOptions":{"allowNonTsExtensions":true,"noEmitForJsFiles":true},"typings":["/Users/so-test/Library/Caches/typescript/3.7/node_modules/@types/react/index.d.ts"],"unresolvedImports":[],"kind":"action::set"}}
{"type":"request","command":"open","arguments":{"file":"/test/App.tsx"}}
Content-Length: 272

{"seq":0,"type":"event","event":"projectLoadingStart","body":{"projectName":"/test/tsconfig.json","reason":"Creating possible configured project for /test/App.tsx to open"}}
Content-Length: 150

{"seq":0,"type":"event","event":"projectLoadingFinish","body":{"projectName":"/test/tsconfig.json"}}
Content-Length: 239

{"seq":0,"type":"event","event":"configFileDiag","body":{"triggerFile":"/test/App.tsx","configFile":"/test/tsconfig.json","diagnostics":[]}}
{"type":"request","command":"quickinfo","arguments":{"offset":9,"line":5,"file":"/test/App.tsx"}}
Content-Length: 283

{"seq":0,"type":"response","command":"quickinfo","success":true,"body":{"kind":"alias","kindModifiers":"","start":{"line":5,"offset":8},"end":{"line":5,"offset":19},"displayString":"(alias) const MyComponent: new () => MyComponent\nimport MyComponent","documentation":"","tags":[]}}

There may be a more efficient way to query the language server, but this is what I came up with in the short amount of time I fiddled with it.

You can look at all the possible request/responses here: https://github.com/Microsoft/TypeScript/blob/master/lib/protocol.d.ts

Hope this helps.

Related