Использование API компилятора машинописи для получения информации о типах полей интерфейса - PullRequest
0 голосов
/ 26 сентября 2018

Рассмотрим следующий интерфейс:

interface X { 
  x: string 
}

Я пытаюсь использовать API компилятора машинописного текста для получения типа свойства x.Вот что у меня получилось:

import {
  PropertySignature,
  createSourceFile,
  ScriptTarget,
  ScriptKind,
  SyntaxKind,
  InterfaceDeclaration,
  Identifier,
} from 'typescript'

describe('Compiler test', () => {
  it('should be able to find type information about X.x', () => {
    const sourceText = 'interface X { x: string }'
    const ast = createSourceFile('source.ts', sourceText, ScriptTarget.ES5, false, ScriptKind.TS)
    const interfaceX = ast
      .getChildAt(0)
      .getChildren()
      .find((child) => child.kind === SyntaxKind.InterfaceDeclaration) as InterfaceDeclaration
    const propX = interfaceX.members.find((member) => (member.name as Identifier).escapedText === 'x')
    console.log(JSON.stringify(propX, null, 2))
  })
})

Теперь содержимое узла propX выглядит следующим образом:

{
  "pos": 13,
  "end": 23,
  "flags": 0,
  "kind": 151,
  "name": {
    "pos": 13,
    "end": 15,
    "flags": 0,
    "escapedText": "x"
  },
  "type": {
    "pos": 16,
    "end": 23,
    "flags": 0,
    "kind": 137
  }
}

Из которого, однако, можно легко извлечь имя узлаузел типа, похоже, не содержит никакой полезной информации.

Как получить информацию о типе свойства? Все, что мне нужно, это "string".

1 Ответ

0 голосов
/ 27 сентября 2018

Таким образом, способ сделать это состоял в том, чтобы построить Program (нужен CompilerHost, чтобы сделать это) и использовать TypeChecker, как @MattMcCutchen предложил:

CompilerHost (вы нене нужна реализация класса, но я нашел ее более удобной):

export const SAMPLE_FILE_NAME = 'sample.ts'

export class TestCompilerHost implements CompilerHost {
  constructor(private readonly code: string) {}
  fileExists = () => true
  getCanonicalFileName = () => SAMPLE_FILE_NAME
  getCurrentDirectory = () => ''
  getDefaultLibFileName = () => 'lib.d.ts'
  getDirectories = () => []
  getNewLine = () => '\n'
  readFile = () => null
  useCaseSensitiveFileNames = () => true
  writeFile = () => {}
  getSourceFile(filename: string): SourceFile {
    return createSourceFile(filename, this.code, ScriptTarget.ES5, true)
  }
}

Создание Program:

const config: CompilerOptions = {
  noResolve: true,
  target: ScriptTarget.ES5,
}
const sourceText = `interface X { x: string }`
const program = createProgram([SAMPLE_FILE_NAME], config, new TestCompilerHost(sourceText))

Найти интерфейс и свойство, как в вопросе (толькоразница в способе доступа к SourceFile):

const ast = program.getSourceFile(SAMPLE_FILE_NAME)
const interfaceX = ast
  .getChildAt(0)
  .getChildren()
  .find((child) => child.kind === SyntaxKind.InterfaceDeclaration) as InterfaceDeclaration
const propX = interfaceX.members.find((member) => (member.name as Identifier).escapedText === 'x')

Наконец получите тип:

const typeChecker = program.getTypeChecker()
const type = typeChecker.getTypeAtLocation(propX.type)
const stringType typeChecker.typeToString(type)

Где propX - та же переменная, что и в моем вопросе.

...