diff --git a/src/index.ts b/src/index.ts index f770eceb3..23a3a04ca 100644 --- a/src/index.ts +++ b/src/index.ts @@ -348,6 +348,7 @@ export interface TsConfigOptions export interface TypeInfo { name: string; comment: string; + kind?: _ts.ScriptElementKind; } /** @@ -831,8 +832,9 @@ export function create(rawOptions: CreateOptions = {}): Service { const info = service.getQuickInfoAtPosition(fileName, position); const name = ts.displayPartsToString(info ? info.displayParts : []); const comment = ts.displayPartsToString(info ? info.documentation : []); + const kind = info?.kind; - return { name, comment }; + return { name, comment, kind }; }; } else { const sys: _ts.System & _ts.FormatDiagnosticsHost = { diff --git a/src/repl.ts b/src/repl.ts index fca8b1bc6..f5ba991f0 100644 --- a/src/repl.ts +++ b/src/repl.ts @@ -8,6 +8,7 @@ import { readFileSync, statSync } from 'fs'; import { Console } from 'console'; import type * as tty from 'tty'; import Module = require('module'); +import { ScriptElementKind } from 'typescript'; /** @internal */ export const EVAL_FILENAME = `[eval].ts`; @@ -306,7 +307,7 @@ function startRepl( } const undo = appendEval(state, identifier); - const { name, comment } = service.getTypeInfo( + let { name, comment, kind } = service.getTypeInfo( state.input, state.path, state.input.length @@ -314,6 +315,29 @@ function startRepl( undo(); + // Check if the user intended to query a Type/Interface + if (kind === '') { + // Workaround to get type information + const undo = appendEval(state, `undefined as unknown as ${identifier}`); + const getTypeInfoRes = service.getTypeInfo( + state.input, + state.path, + state.input.length + ); + + undo(); + + if ( + [ + ScriptElementKind.typeElement, + ScriptElementKind.interfaceElement, + ].includes(getTypeInfoRes.kind!) + ) { + name = getTypeInfoRes.name; + comment = getTypeInfoRes.comment; + } + } + if (name) repl.outputStream.write(`${name}\n`); if (comment) repl.outputStream.write(`${comment}\n`); repl.displayPrompt(); diff --git a/src/test/index.spec.ts b/src/test/index.spec.ts index c8c8a7e4b..741be58a1 100644 --- a/src/test/index.spec.ts +++ b/src/test/index.spec.ts @@ -401,6 +401,7 @@ test.suite('ts-node', (test) => { expect(stdout).to.equal('> 123\n' + 'undefined\n' + '> '); }); + // TODO: REPL shouldn't require a leading `\n` to properly work on Windows test('REPL has command to get type information', async () => { const execPromise = exec(`${cmd} --interactive`); execPromise.child.stdin!.end('\nconst a = 123\n.type a'); @@ -411,6 +412,36 @@ test.suite('ts-node', (test) => { ); }); + // TODO: Same as above + // TODO2: Merge with below test once #1126 is fixed + test('REPL "type" command can be used on types', async () => { + const execPromise = exec(`${cmd} --interactive`); + execPromise.child.stdin!.end( + '\n' + 'type Foo = string | { x: 1 }\n' + '.type Foo\n' + ); + const { err, stdout } = await execPromise; + expect(err).to.equal(null); + expect(stdout).to.equal( + '> undefined\n' + + '> undefined\n' + + '> type Foo = string | {\n x: 1;\n}\n' + + '> ' + ); + }); + + // TODO: Same as above + test('REPL "type" command can be used on interfaces', async () => { + const execPromise = exec(`${cmd} --interactive`); + execPromise.child.stdin!.end( + '\n' + 'interface Bar { x: string; y: number; }\n' + '.type Bar' + ); + const { err, stdout } = await execPromise; + expect(err).to.equal(null); + expect(stdout).to.equal( + '> undefined\n' + '> undefined\n' + '> interface Bar\n' + '> ' + ); + }); + function createReplViaApi() { const stdin = new PassThrough(); const stdout = new PassThrough(); @@ -1654,6 +1685,7 @@ test.suite('ts-node', (test) => { service.getTypeInfo('/**jsdoc here*/const x = 10', 'test.ts', 21) ).to.deep.equal({ comment: 'jsdoc here', + kind: 'const', name: 'const x: 10', }); }); @@ -1664,6 +1696,7 @@ test.suite('ts-node', (test) => { service.getTypeInfo('/**jsdoc here*/const x = 10', 'test.ts', 0) ).to.deep.equal({ comment: '', + kind: undefined, name: '', }); });