Consider the following interface:
interface X {
x: string
}
I'm trying to use the typescript compiler API to get property x
's type. Here's what I have so far:
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))
})
})
Now the content of the propX
node is the following:
{
"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
}
}
From which the name of the node is clearly extractable, however the type node doesn't seem to have any information that's useful.
How would I get the type information of the property? All I need is "string"
.
So the way to do it was to build a Program
(need a CompilerHost
to do it) and use the TypeChecker
as @MattMcCutchen suggested:
The CompilerHost
(you don't need a class implementation, but I found it more convenient):
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)
}
}
Build a 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))
Find the interface and property like in the question (only difference is the way of accessing the 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')
Lastly get the type:
const typeChecker = program.getTypeChecker()
const type = typeChecker.getTypeAtLocation(propX.type)
const stringType = typeChecker.typeToString(type)
Where propX
is the same variable as in my question.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With