Skip to content

Instantly share code, notes, and snippets.

@brandonkal
Last active June 18, 2019 18:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save brandonkal/a029f0f2dbc600d69d0ba6ffb83d5154 to your computer and use it in GitHub Desktop.
Save brandonkal/a029f0f2dbc600d69d0ba6ffb83d5154 to your computer and use it in GitHub Desktop.
Typescript Return type from compiled string
import ts from 'typescript'
import fs from 'fs'
import path from 'path'
function transform(
contents: string,
libSource: string,
compilerOptions: ts.CompilerOptions = {}
) {
if (!compilerOptions.target) {
compilerOptions.target = ts.ScriptTarget.ES2015
}
// Generated outputs
let outputs: any[] = []
// Create a compilerHost object to allow the compiler to read and write files
let compilerHost = {
getSourceFile: function(filename: string, languageVersion: any) {
if (filename === 'file.ts')
return ts.createSourceFile(
filename,
contents,
compilerOptions.target!,
false
)
if (filename === 'lib.d.ts')
return ts.createSourceFile(
filename,
libSource,
compilerOptions.target!,
false
)
return undefined
},
writeFile: function(
name: string,
text: string,
writeByteOrderMark: boolean
) {
outputs.push({
name: name,
text: text,
writeByteOrderMark: writeByteOrderMark,
})
},
getDefaultLibFileName: function(options: ts.CompilerOptions) {
return 'lib.d.ts'
},
getDefaultLibName: function(filename: string, languageVersion: any) {
return 'lib.d.ts'
},
useCaseSensitiveFileNames: function() {
return false
},
getCanonicalFileName: function(filename: string) {
return filename
},
getCurrentDirectory: function() {
return ''
},
getNewLine: function() {
return '\n'
},
fileExists: function() {
return true
},
readFile: function() {
return ''
},
}
// Create a program from inputs
let program = ts.createProgram(['file.ts'], compilerOptions, compilerHost)
// Query for early errors
let errors = program.getGlobalDiagnostics()
let checker = program.getTypeChecker()
let files = program.getSourceFiles()
let theType: ts.Type | undefined = undefined
// Find type from export
files.forEach(file => {
if (file.fileName === 'file.ts') {
console.log(file)
file.forEachChild(node => {
if (theType) return
if (
node.kind === ts.SyntaxKind.VariableStatement &&
node.modifiers &&
node.modifiers[0].kind === ts.SyntaxKind.ExportKeyword
) {
node.forEachChild(child => {
if (theType) return
console.log('children')
console.log(child)
if (child.kind === ts.SyntaxKind.VariableDeclarationList) {
child.forEachChild(decl => {
if (theType) return
theType = checker.getTypeAtLocation(decl)
})
}
})
}
})
}
})
let type: ts.Type
let returnInfo
interface TypeInfo {
value?: any
kind?: string
}
let typeArray: TypeInfo[] = []
if (theType !== undefined) {
type = theType as ts.Type
let getKind = (flags: ts.TypeFlags) => {
return flags === ts.TypeFlags.Number
? 'number'
: flags === ts.TypeFlags.String
? 'string'
: flags === ts.TypeFlags.StringLiteral
? 'stringLiteral'
: flags === ts.TypeFlags.NumberLiteral
? 'numberLiteral'
: 'other'
}
if (type.isUnion()) {
type.types.forEach(current => {
if (current.isLiteral()) {
current.value
typeArray.push({
value: current.value,
kind: getKind(current.flags),
})
} else {
typeArray.push({
kind: getKind(current.flags),
})
}
})
}
let value: boolean | string | ts.PseudoBigInt | number = false
if (type.isLiteral()) value = type.value
returnInfo = {
union: type.isUnion(),
kind: getKind(type.flags),
types: typeArray,
value: value,
}
}
return {
outputs: outputs,
type: returnInfo,
errors: errors,
}
}
// Calling our transform function and loading the default library:
let source = "export const x = (1 == 2 && 'awesome') || 'not cool'"
let libSource = fs
.readFileSync(
path.join(path.dirname(require.resolve('typescript')), 'lib.d.ts')
)
.toString()
let result = transform(source, libSource)
debugger
console.log(JSON.stringify(result))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment