in compiler/src/model/utils.ts [78:368]
export function modelType (node: Node): model.ValueOf {
switch (node.getKind()) {
case ts.SyntaxKind.BooleanKeyword: {
const type: model.InstanceOf = {
kind: 'instance_of',
type: {
name: 'boolean',
namespace: '_builtins'
}
}
return type
}
case ts.SyntaxKind.StringKeyword: {
const type: model.InstanceOf = {
kind: 'instance_of',
type: {
name: 'string',
namespace: '_builtins'
}
}
return type
}
case ts.SyntaxKind.NumberKeyword: {
const type: model.InstanceOf = {
kind: 'instance_of',
type: {
name: 'number',
namespace: '_builtins'
}
}
return type
}
case ts.SyntaxKind.NullKeyword: {
const type: model.InstanceOf = {
kind: 'instance_of',
type: {
name: 'null',
namespace: '_builtins'
}
}
return type
}
case ts.SyntaxKind.VoidKeyword: {
const type: model.InstanceOf = {
kind: 'instance_of',
type: {
name: 'void',
namespace: '_builtins'
}
}
return type
}
// TODO: this should not be used in the specification
// we should throw an error
case ts.SyntaxKind.AnyKeyword: {
const type: model.InstanceOf = {
kind: 'instance_of',
type: {
name: 'object',
namespace: '_builtins'
}
}
return type
}
case ts.SyntaxKind.ArrayType: {
const kinds: ts.SyntaxKind[] = [
ts.SyntaxKind.StringKeyword,
ts.SyntaxKind.BooleanKeyword,
ts.SyntaxKind.AnyKeyword,
ts.SyntaxKind.ArrayType,
ts.SyntaxKind.TypeReference
]
let children: Node[] = []
node.forEachChild(child => children.push(child))
children = children.filter(child => kinds.some(kind => kind === child.getKind()))
assert(node, children.length === 1, `Expected array to have 1 usable child but saw ${children.length}`)
const type: model.ArrayOf = {
kind: 'array_of',
value: modelType(children[0])
}
return type
}
case ts.SyntaxKind.UnionType: {
assert(node, Node.isUnionTypeNode(node), `The node is not of type ${ts.SyntaxKind[ts.SyntaxKind.UnionType]} but ${ts.SyntaxKind[node.getKind()]} instead`)
const type: model.UnionOf = {
kind: 'union_of',
items: node.getTypeNodes().map(node => modelType(node))
}
return type
}
case ts.SyntaxKind.LiteralType: {
assert(node, Node.isLiteralTypeNode(node), `The node is not of type ${ts.SyntaxKind[ts.SyntaxKind.LiteralType]} but ${ts.SyntaxKind[node.getKind()]} instead`)
return modelType(node.getLiteral())
}
case ts.SyntaxKind.StringLiteral: {
assert(node, Node.isStringLiteral(node), `The node is not of type ${ts.SyntaxKind[ts.SyntaxKind.StringLiteral]} but ${ts.SyntaxKind[node.getKind()]} instead`)
const type: model.LiteralValue = {
kind: 'literal_value',
value: node.getText().replace(/'/g, '')
}
return type
}
case ts.SyntaxKind.TrueKeyword: {
assert(node, Node.isTrueLiteral(node), `The node is not of type ${ts.SyntaxKind[ts.SyntaxKind.TrueKeyword]} but ${ts.SyntaxKind[node.getKind()]} instead`)
const type: model.LiteralValue = {
kind: 'literal_value',
value: true
}
return type
}
case ts.SyntaxKind.FalseKeyword: {
assert(node, Node.isFalseLiteral(node), `The node is not of type ${ts.SyntaxKind[ts.SyntaxKind.FalseKeyword]} but ${ts.SyntaxKind[node.getKind()]} instead`)
const type: model.LiteralValue = {
kind: 'literal_value',
value: false
}
return type
}
case ts.SyntaxKind.NumericLiteral: {
assert(node, Node.isNumericLiteral(node), `The node is not of type ${ts.SyntaxKind[ts.SyntaxKind.NumericLiteral]} but ${ts.SyntaxKind[node.getKind()]} instead`)
const type: model.LiteralValue = {
kind: 'literal_value',
value: Number(node.getText())
}
return type
}
case ts.SyntaxKind.PrefixUnaryExpression: {
// Negative number
const type: model.LiteralValue = {
kind: 'literal_value',
value: Number(node.getText())
}
return type
}
case ts.SyntaxKind.TypeParameter: {
assert(node, Node.isTypeParameterDeclaration(node), `The node is not of type ${ts.SyntaxKind[ts.SyntaxKind.TypeReference]} but ${ts.SyntaxKind[node.getKind()]} instead`)
const name = node.compilerNode.getText()
const type: model.InstanceOf = {
kind: 'instance_of',
type: {
name,
namespace: getNameSpace(node)
}
}
return type
}
case ts.SyntaxKind.TypeReference: {
// TypeReferences are fun types, it's basically how TypeScript defines
// everything that is not a basic type, an interface or a class will
// appear here when used for instance.
// For some reason also the `Array` type (the one defined with `Array<T>` and not `T[]`)
// is interpreted as TypeReference as well.
// The two most important fields of a TypeReference are `typeName` and `typeArguments`,
// the first one is the name of the type (eg: `Foo`), while the second is the
// possible generics (eg: Foo<T> => T will be in typeArguments).
assert(node, Node.isTypeReference(node), `The node is not of type ${ts.SyntaxKind[ts.SyntaxKind.TypeReference]} but ${ts.SyntaxKind[node.getKind()]} instead`)
const identifier = node.getTypeName()
assert(node, Node.isIdentifier(identifier), 'Should be an identifier')
const name = identifier.compilerNode.escapedText as string
switch (name) {
case 'Array': {
assert(node, node.getTypeArguments().length === 1, 'An array must have one argument')
const [value] = node.getTypeArguments().map(node => modelType(node))
const type: model.ArrayOf = {
kind: 'array_of',
value
}
return type
}
case 'ArrayBuffer': {
const type: model.InstanceOf = {
kind: 'instance_of',
type: {
name: 'binary',
namespace: '_builtins'
}
}
return type
}
case 'Dictionary':
case 'AdditionalProperties': {
assert(node, node.getTypeArguments().length === 2, 'A Dictionary must have two arguments')
const [key, value] = node.getTypeArguments().map(node => modelType(node))
const type: model.DictionaryOf = {
kind: 'dictionary_of',
key,
value,
singleKey: false
}
return type
}
case 'SingleKeyDictionary':
case 'AdditionalProperty': {
assert(node, node.getTypeArguments().length === 2, 'A SingleKeyDictionary must have two arguments')
const [key, value] = node.getTypeArguments().map(node => modelType(node))
const type: model.DictionaryOf = {
kind: 'dictionary_of',
key,
value,
singleKey: true
}
return type
}
case 'UserDefinedValue': {
const type: model.UserDefinedValue = {
kind: 'user_defined_value'
}
return type
}
default: {
const generics = node.getTypeArguments().map(node => modelType(node))
const identifier = node.getTypeName()
assert(node, Node.isIdentifier(identifier), 'Not an identifier')
assert(node, identifier.getDefinitions().length > 0, 'Unknown definition (missing import?)')
const declaration = identifier.getDefinitions()[0].getDeclarationNode()
// We are looking at a generic parameter
if (declaration == null) {
const type: model.InstanceOf = {
kind: 'instance_of',
...(generics.length > 0 && { generics }),
type: {
name,
namespace: getNameSpace(node)
}
}
return type
}
assert(
node,
Node.isClassDeclaration(declaration) ||
Node.isInterfaceDeclaration(declaration) ||
Node.isEnumDeclaration(declaration) ||
Node.isTypeAliasDeclaration(declaration) ||
Node.isTypeParameterDeclaration(declaration),
'It should be a class, interface, enum, type alias, or type parameter declaration'
)
const type: model.InstanceOf = {
kind: 'instance_of',
...(generics.length > 0 && { generics }),
type: {
name: declaration.getName() as string,
namespace: getNameSpace(node)
}
}
if (Node.isTypeParameterDeclaration(declaration)) {
const parent = declaration.getParent()
assert(
parent,
Node.isClassDeclaration(parent) ||
Node.isInterfaceDeclaration(parent) ||
Node.isTypeAliasDeclaration(parent),
'It should be a class, interface, or type alias declaration'
)
type.type.namespace = `${type.type.namespace}.${parent.getName() as string}`
}
return type
}
}
}
default:
assert(node, false, `Unhandled node kind: ${ts.SyntaxKind[node.getKind()]}`)
}
}