diff --git a/packages/babel-generator/test/index.js b/packages/babel-generator/test/index.js index ee952fb30b..75ce04c6f9 100644 --- a/packages/babel-generator/test/index.js +++ b/packages/babel-generator/test/index.js @@ -11,6 +11,7 @@ import fixtures from "babel-helper-fixtures"; describe("generation", function() { it("completeness", function() { Object.keys(t.VISITOR_KEYS).forEach(function(type) { + if (type.startsWith("TS")) return; // TODO assert.ok(!!Printer.prototype[type], type + " should exist"); }); diff --git a/packages/babel-types/src/definitions/core.js b/packages/babel-types/src/definitions/core.js index b99efe79d1..4dbfa55445 100644 --- a/packages/babel-types/src/definitions/core.js +++ b/packages/babel-types/src/definitions/core.js @@ -118,8 +118,10 @@ defineType("BreakStatement", { aliases: ["Statement", "Terminatorless", "CompletionStatement"], }); -defineType("CallExpression", { - visitor: ["callee", "arguments"], +const callOrNew = { + visitor: ["callee", "arguments", "typeParameters"], + builder: ["callee", "arguments"], + aliases: ["Expression"], fields: { callee: { validate: assertNodeType("Expression"), @@ -134,9 +136,14 @@ defineType("CallExpression", { validate: assertOneOf(true, false), optional: true, }, + typeParameters: { + validate: assertNodeType("TypeParameterInstantiation"), + optional: true, + }, }, - aliases: ["Expression"], -}); +}; + +defineType("CallExpression", callOrNew); defineType("CatchClause", { visitor: ["param", "body"], @@ -265,30 +272,51 @@ defineType("ForStatement", { }, }); +export const functionCommon = { + params: { + validate: chain( + assertValueType("array"), + assertEach(assertNodeType("LVal")), + ), + }, + generator: { + default: false, + validate: assertValueType("boolean"), + }, + async: { + validate: assertValueType("boolean"), + default: false, + }, + returnType: { + validate: assertNodeType("TypeAnnotation", "Noop"), + optional: true, + }, + typeParameters: { + validate: assertNodeType("TypeParameterDeclaration", "Noop"), + optional: true, + }, +}; + +export const functionDeclarationCommon = { + ...functionCommon, + declare: { + validate: assertValueType("boolean"), + optional: true, + }, + id: { + validate: assertNodeType("Identifier"), + optional: true, // May be null for `export default function` + }, +}; + defineType("FunctionDeclaration", { builder: ["id", "params", "body", "generator", "async"], visitor: ["id", "params", "body", "returnType", "typeParameters"], fields: { - id: { - validate: assertNodeType("Identifier"), - }, - params: { - validate: chain( - assertValueType("array"), - assertEach(assertNodeType("LVal")), - ), - }, + ...functionDeclarationCommon, body: { validate: assertNodeType("BlockStatement"), }, - generator: { - default: false, - validate: assertValueType("boolean"), - }, - async: { - default: false, - validate: assertValueType("boolean"), - }, }, aliases: [ "Scopable", @@ -312,35 +340,37 @@ defineType("FunctionExpression", { "Pureish", ], fields: { + ...functionCommon, id: { validate: assertNodeType("Identifier"), optional: true, }, - params: { - validate: chain( - assertValueType("array"), - assertEach(assertNodeType("LVal")), - ), - }, body: { validate: assertNodeType("BlockStatement"), }, - generator: { - default: false, - validate: assertValueType("boolean"), - }, - async: { - default: false, - validate: assertValueType("boolean"), - }, }, }); +export const patternLikeCommon = { + typeAnnotation: { + // TODO: babel-plugin-transform-flow-comments puts a Noop here, is there a better way? + validate: assertNodeType("TypeAnnotation", "Noop"), + optional: true, + }, + decorators: { + validate: chain( + assertValueType("array"), + assertEach(assertNodeType("Decorator")), + ), + }, +}; + defineType("Identifier", { builder: ["name"], visitor: ["typeAnnotation"], - aliases: ["Expression", "LVal"], + aliases: ["Expression", "PatternLike", "LVal", "TSEntityName"], fields: { + ...patternLikeCommon, name: { validate(node, key, val) { if (!t.isValidIdentifier(val)) { @@ -348,11 +378,9 @@ defineType("Identifier", { } }, }, - decorators: { - validate: chain( - assertValueType("array"), - assertEach(assertNodeType("Decorator")), - ), + optional: { + validate: assertValueType("boolean"), + optional: true, }, }, }); @@ -483,30 +511,15 @@ defineType("MemberExpression", { }, }); -defineType("NewExpression", { - visitor: ["callee", "arguments"], - aliases: ["Expression"], - fields: { - callee: { - validate: assertNodeType("Expression"), - }, - arguments: { - validate: chain( - assertValueType("array"), - assertEach(assertNodeType("Expression", "SpreadElement")), - ), - }, - optional: { - validate: assertOneOf(true, false), - optional: true, - }, - }, -}); +defineType("NewExpression", callOrNew); defineType("Program", { visitor: ["directives", "body"], builder: ["body", "directives"], fields: { + sourceFile: { + validate: assertValueType("string"), + }, directives: { validate: chain( assertValueType("array"), @@ -542,6 +555,7 @@ defineType("ObjectExpression", { defineType("ObjectMethod", { builder: ["kind", "key", "params", "body", "computed"], fields: { + ...functionCommon, kind: { validate: chain( assertValueType("string"), @@ -555,12 +569,12 @@ defineType("ObjectMethod", { }, key: { validate: (function() { - const normal = assertNodeType("Expression"); - const computed = assertNodeType( + const normal = assertNodeType( "Identifier", "StringLiteral", "NumericLiteral", ); + const computed = assertNodeType("Expression"); return function(node, key, val) { const validator = node.computed ? computed : normal; @@ -577,14 +591,6 @@ defineType("ObjectMethod", { body: { validate: assertNodeType("BlockStatement"), }, - generator: { - default: false, - validate: assertValueType("boolean"), - }, - async: { - default: false, - validate: assertValueType("boolean"), - }, }, visitor: [ "key", @@ -628,7 +634,9 @@ defineType("ObjectProperty", { })(), }, value: { - validate: assertNodeType("Expression", "Pattern", "RestElement"), + // Value may be PatternLike if this is an AssignmentProperty + // https://github.com/babel/babylon/issues/434 + validate: assertNodeType("Expression", "PatternLike"), }, shorthand: { validate: assertValueType("boolean"), @@ -648,17 +656,13 @@ defineType("ObjectProperty", { defineType("RestElement", { visitor: ["argument", "typeAnnotation"], - aliases: ["LVal"], + builder: ["argument"], + aliases: ["LVal", "PatternLike"], fields: { + ...patternLikeCommon, argument: { validate: assertNodeType("LVal"), }, - decorators: { - validate: chain( - assertValueType("array"), - assertEach(assertNodeType("Decorator")), - ), - }, }, }); @@ -742,7 +746,7 @@ defineType("TryStatement", { }, handler: { optional: true, - handler: assertNodeType("BlockStatement"), + validate: assertNodeType("BlockStatement"), }, finalizer: { optional: true, @@ -790,6 +794,10 @@ defineType("VariableDeclaration", { visitor: ["declarations"], aliases: ["Statement", "Declaration"], fields: { + declare: { + validate: assertValueType("boolean"), + optional: true, + }, kind: { validate: chain( assertValueType("string"), diff --git a/packages/babel-types/src/definitions/es2015.js b/packages/babel-types/src/definitions/es2015.js index 4df89a0dd1..63c131597e 100644 --- a/packages/babel-types/src/definitions/es2015.js +++ b/packages/babel-types/src/definitions/es2015.js @@ -7,11 +7,14 @@ import defineType, { assertEach, assertOneOf, } from "./index"; +import { functionCommon, patternLikeCommon } from "./core"; defineType("AssignmentPattern", { visitor: ["left", "right"], - aliases: ["Pattern", "LVal"], + builder: ["left", "right"], + aliases: ["Pattern", "PatternLike", "LVal"], fields: { + ...patternLikeCommon, left: { validate: assertNodeType("Identifier", "ObjectPattern", "ArrayPattern"), }, @@ -29,12 +32,14 @@ defineType("AssignmentPattern", { defineType("ArrayPattern", { visitor: ["elements", "typeAnnotation"], - aliases: ["Pattern", "LVal"], + builder: ["elements"], + aliases: ["Pattern", "PatternLike", "LVal"], fields: { + ...patternLikeCommon, elements: { validate: chain( assertValueType("array"), - assertEach(assertNodeType("Identifier", "Pattern", "RestElement")), + assertEach(assertNodeType("PatternLike")), ), }, decorators: { @@ -58,19 +63,14 @@ defineType("ArrowFunctionExpression", { "Pureish", ], fields: { - params: { - validate: chain( - assertValueType("array"), - assertEach(assertNodeType("LVal")), - ), + ...functionCommon, + expression: { + // https://github.com/babel/babylon/issues/505 + validate: assertValueType("boolean"), }, body: { validate: assertNodeType("BlockStatement", "Expression"), }, - async: { - validate: assertValueType("boolean"), - default: false, - }, }, }); @@ -80,12 +80,46 @@ defineType("ClassBody", { body: { validate: chain( assertValueType("array"), - assertEach(assertNodeType("ClassMethod", "ClassProperty")), + assertEach( + assertNodeType( + "ClassMethod", + "ClassProperty", + "TSDeclareMethod", + "TSIndexSignature", + ), + ), ), }, }, }); +const classCommon = { + typeParameters: { + validate: assertNodeType("TypeParameterDeclaration", "Noop"), + optional: true, + }, + body: { + validate: assertNodeType("ClassBody"), + }, + superClass: { + optional: true, + validate: assertNodeType("Expression"), + }, + superTypeParameters: { + validate: assertNodeType("TypeParameterInstantiation"), + optional: true, + }, + implements: { + validate: chain( + assertValueType("array"), + assertEach( + assertNodeType("TSExpressionWithTypeArguments", "FlowClassImplements"), + ), + ), + optional: true, + }, +}; + defineType("ClassDeclaration", { builder: ["id", "superClass", "body", "decorators"], visitor: [ @@ -100,21 +134,25 @@ defineType("ClassDeclaration", { ], aliases: ["Scopable", "Class", "Statement", "Declaration", "Pureish"], fields: { + ...classCommon, + declare: { + validate: assertValueType("boolean"), + optional: true, + }, + abstract: { + validate: assertValueType("boolean"), + optional: true, + }, id: { validate: assertNodeType("Identifier"), - }, - body: { - validate: assertNodeType("ClassBody"), - }, - superClass: { - optional: true, - validate: assertNodeType("Expression"), + optional: true, // Missing if this is the child of an ExportDefaultDeclaration. }, decorators: { validate: chain( assertValueType("array"), assertEach(assertNodeType("Decorator")), ), + optional: true, }, }, }); @@ -123,6 +161,7 @@ defineType("ClassExpression", { inherits: "ClassDeclaration", aliases: ["Scopable", "Class", "Expression", "Pureish"], fields: { + ...classCommon, id: { optional: true, validate: assertNodeType("Identifier"), @@ -139,6 +178,7 @@ defineType("ClassExpression", { assertValueType("array"), assertEach(assertNodeType("Decorator")), ), + optional: true, }, }, }); @@ -170,6 +210,7 @@ defineType("ExportDefaultDeclaration", { declaration: { validate: assertNodeType( "FunctionDeclaration", + "TSDeclareFunction", "ClassDeclaration", "Expression", ), @@ -316,6 +357,73 @@ defineType("MetaProperty", { }, }); +export const classMethodOrPropertyCommon = { + abstract: { + validate: assertValueType("boolean"), + optional: true, + }, + accessibility: { + validate: chain( + assertValueType("string"), + assertOneOf("public", "private", "protected"), + ), + optional: true, + }, + static: { + validate: assertValueType("boolean"), + optional: true, + }, + computed: { + default: false, + validate: assertValueType("boolean"), + }, + optional: { + validate: assertValueType("boolean"), + optional: true, + }, + key: { + validate: (function() { + const normal = assertNodeType( + "Identifier", + "StringLiteral", + "NumericLiteral", + ); + const computed = assertNodeType("Expression"); + + return function(node, key, val) { + const validator = node.computed ? computed : normal; + validator(node, key, val); + }; + })(), + }, +}; + +export const classMethodOrDeclareMethodCommon = { + ...functionCommon, + ...classMethodOrPropertyCommon, + kind: { + validate: chain( + assertValueType("string"), + assertOneOf("get", "set", "method", "constructor"), + ), + default: "method", + }, + access: { + validate: chain( + assertValueType("string"), + assertOneOf("public", "private", "protected"), + ), + optional: true, + }, + decorators: { + validate: chain( + assertValueType("array"), + assertEach(assertNodeType("Decorator")), + ), + optional: true, + }, +}; + defineType("ClassMethod", { aliases: ["Function", "Scopable", "BlockParent", "FunctionParent", "Method"], builder: ["kind", "key", "params", "body", "computed", "static"], @@ -328,70 +436,23 @@ defineType("ClassMethod", { "typeParameters", ], fields: { - kind: { - validate: chain( - assertValueType("string"), - assertOneOf("get", "set", "method", "constructor"), - ), - default: "method", - }, - computed: { - default: false, - validate: assertValueType("boolean"), - }, - static: { - default: false, - validate: assertValueType("boolean"), - }, - key: { - validate: (function() { - const normal = assertNodeType("Expression"); - const computed = assertNodeType( - "Identifier", - "StringLiteral", - "NumericLiteral", - ); - - return function(node, key, val) { - const validator = node.computed ? computed : normal; - validator(node, key, val); - }; - })(), - }, - params: { - validate: chain( - assertValueType("array"), - assertEach(assertNodeType("LVal")), - ), - }, + ...classMethodOrDeclareMethodCommon, body: { validate: assertNodeType("BlockStatement"), }, - generator: { - default: false, - validate: assertValueType("boolean"), - }, - async: { - default: false, - validate: assertValueType("boolean"), - }, }, }); defineType("ObjectPattern", { visitor: ["properties", "typeAnnotation"], - aliases: ["Pattern", "LVal"], + builder: ["properties"], + aliases: ["Pattern", "PatternLike", "LVal"], fields: { + ...patternLikeCommon, properties: { validate: chain( assertValueType("array"), - assertEach(assertNodeType("RestElement", "Property")), - ), - }, - decorators: { - validate: chain( - assertValueType("array"), - assertEach(assertNodeType("Decorator")), + assertEach(assertNodeType("RestElement", "ObjectProperty")), ), }, }, diff --git a/packages/babel-types/src/definitions/flow.js b/packages/babel-types/src/definitions/flow.js index 1ef3c72248..dccf11d551 100644 --- a/packages/babel-types/src/definitions/flow.js +++ b/packages/babel-types/src/definitions/flow.js @@ -1,4 +1,4 @@ -import defineType, { assertValueType } from "./index"; +import defineType from "./index"; defineType("AnyTypeAnnotation", { aliases: ["Flow", "FlowBaseAnnotation"], @@ -40,19 +40,6 @@ defineType("ClassImplements", { }, }); -defineType("ClassProperty", { - visitor: ["key", "value", "typeAnnotation", "decorators"], - builder: ["key", "value", "typeAnnotation", "decorators", "computed"], - aliases: ["Property"], - fields: { - computed: { - validate: assertValueType("boolean"), - default: false, - }, - // todo - }, -}); - defineType("DeclareClass", { visitor: ["id", "typeParameters", "extends", "body"], aliases: ["Flow", "FlowDeclaration", "Statement", "Declaration"], @@ -265,14 +252,6 @@ defineType("TypeAlias", { }, }); -defineType("TypeAnnotation", { - visitor: ["typeAnnotation"], - aliases: ["Flow"], - fields: { - // todo - }, -}); - defineType("TypeCastExpression", { visitor: ["expression", "typeAnnotation"], aliases: ["Flow", "ExpressionWrapper", "Expression"], @@ -281,30 +260,6 @@ defineType("TypeCastExpression", { }, }); -defineType("TypeParameter", { - visitor: ["bound"], - aliases: ["Flow"], - fields: { - // todo - }, -}); - -defineType("TypeParameterDeclaration", { - visitor: ["params"], - aliases: ["Flow"], - fields: { - // todo - }, -}); - -defineType("TypeParameterInstantiation", { - visitor: ["params"], - aliases: ["Flow"], - fields: { - // todo - }, -}); - defineType("ObjectTypeAnnotation", { visitor: ["properties", "indexers", "callProperties"], aliases: ["Flow"], diff --git a/packages/babel-types/src/definitions/init.js b/packages/babel-types/src/definitions/init.js index 4a82f02e92..3fe703aaf0 100644 --- a/packages/babel-types/src/definitions/init.js +++ b/packages/babel-types/src/definitions/init.js @@ -5,3 +5,5 @@ import "./flow"; import "./jsx"; import "./misc"; import "./experimental"; +import "./tsFlowCommon"; +import "./typescript"; diff --git a/packages/babel-types/src/definitions/tsFlowCommon.js b/packages/babel-types/src/definitions/tsFlowCommon.js new file mode 100644 index 0000000000..ee45f78075 --- /dev/null +++ b/packages/babel-types/src/definitions/tsFlowCommon.js @@ -0,0 +1,96 @@ +// @flow + +import defineType, { + assertEach, + assertNodeType, + assertValueType, + chain, +} from "./index"; +import { classMethodOrPropertyCommon } from "./es2015"; + +defineType("TypeAnnotation", { + aliases: ["Flow"], + visitor: ["typeAnnotation"], + fields: { + typeAnnotation: { + validate: assertNodeType("TSType", "Flow"), + }, + }, +}); + +defineType("TypeParameterInstantiation", { + visitor: ["params"], + aliases: ["Flow"], + fields: { + params: { + validate: chain( + assertValueType("array"), + assertEach(assertNodeType("TSType", "FlowType")), + ), + }, + }, +}); + +defineType("TypeParameterDeclaration", { + aliases: ["Flow"], + visitor: ["params"], + fields: { + params: { + validate: chain( + assertValueType("array"), + assertEach(assertNodeType("TypeParameter")), + ), + }, + }, +}); + +defineType("TypeParameter", { + aliases: ["Flow"], + visitor: ["bound", "constraint", "default"], + fields: { + name: { + validate: assertValueType("string"), + }, + bound: { + validate: assertNodeType("TypeAnnotation"), + optional: true, + }, + constraint: { + validate: assertNodeType("TSType"), + optional: true, + }, + default: { + validate: assertNodeType("TSType", "Flow"), + optional: true, + }, + }, +}); + +defineType("ClassProperty", { + visitor: ["key", "value", "typeAnnotation", "decorators"], + builder: ["key", "value", "typeAnnotation", "decorators", "computed"], + aliases: ["Property"], + fields: { + ...classMethodOrPropertyCommon, + value: { + validate: assertNodeType("Expression"), + optional: true, + }, + typeAnnotation: { + validate: assertNodeType("TypeAnnotation", "Noop"), + optional: true, + }, + decorators: { + validate: chain( + assertValueType("array"), + assertEach(assertNodeType("Decorator")), + ), + optional: true, + }, + readonly: { + validate: assertValueType("boolean"), + optional: true, + }, + // todo: Has optional "variance" property for flow plugin + }, +}); diff --git a/packages/babel-types/src/definitions/typescript.js b/packages/babel-types/src/definitions/typescript.js new file mode 100644 index 0000000000..e75918dba8 --- /dev/null +++ b/packages/babel-types/src/definitions/typescript.js @@ -0,0 +1,426 @@ +// @flow + +import defineType, { + assertEach, + assertNodeType, + assertOneOf, + assertValueType, + chain, +} from "./index"; +import { functionDeclarationCommon } from "./core"; +import { classMethodOrDeclareMethodCommon } from "./es2015"; + +const bool = assertValueType("boolean"); + +function validate(validate) { + return { validate }; +} + +function typeIs(typeName) { + return typeof typeName === "string" + ? assertNodeType(typeName) + : assertNodeType(...typeName); +} + +function validateType(name) { + return validate(typeIs(name)); +} + +function validateOptional(validate) { + return { validate, optional: true }; +} + +function validateOptionalType(typeName) { + return { validate: typeIs(typeName), optional: true }; +} + +function arrayOf(elementType) { + return chain(assertValueType("array"), assertEach(elementType)); +} + +function arrayOfType(nodeTypeName) { + return arrayOf(typeIs(nodeTypeName)); +} + +function validateArrayOfType(nodeTypeName) { + return validate(arrayOfType(nodeTypeName)); +} + +defineType("TSParameterProperty", { + aliases: ["LVal"], // TODO: This isn't usable in general as an LVal. Should have a "Parameter" alias. + visitor: ["parameter"], + fields: { + accessibility: { + validate: assertOneOf("public", "private", "protected"), + optional: true, + }, + readonly: { + validate: assertValueType("boolean"), + optional: true, + }, + parameter: { + validate: assertNodeType("Identifier", "AssignmentPattern"), + }, + }, +}); + +defineType("TSDeclareFunction", { + aliases: ["Statement", "Declaration"], + visitor: ["id", "typeParameters", "params", "returnType"], + fields: functionDeclarationCommon, +}); + +defineType("TSDeclareMethod", { + visitor: ["decorators", "key", "typeParameters", "params", "returnType"], + fields: classMethodOrDeclareMethodCommon, +}); + +defineType("TSQualifiedName", { + aliases: ["TSEntityName"], + visitor: ["left", "right"], + fields: { + left: validateType("TSEntityName"), + right: validateType("Identifier"), + }, +}); + +const signatureDeclarationCommon = { + typeParameters: validateOptionalType("TypeParameterDeclaration"), + parameters: validateArrayOfType(["Identifier", "RestElement"]), + typeAnnotation: validateOptionalType("TypeAnnotation"), +}; + +const callConstructSignatureDeclaration = { + aliases: ["TSTypeElement"], + visitor: ["typeParameters", "parameters", "typeAnnotation"], + fields: signatureDeclarationCommon, +}; + +defineType("TSCallSignatureDeclaration", callConstructSignatureDeclaration); +defineType( + "TSConstructSignatureDeclaration", + callConstructSignatureDeclaration, +); + +const namedTypeElementCommon = { + key: validateType("Expression"), + computed: validate(bool), + optional: validateOptional(bool), +}; + +defineType("TSPropertySignature", { + aliases: ["TSTypeElement"], + visitor: ["key", "typeAnnotation", "initializer"], + fields: { + ...namedTypeElementCommon, + readonly: validateOptional(bool), + typeAnnotation: validateOptionalType("TypeAnnotation"), + initializer: validateOptionalType("Expresssion"), + }, +}); + +defineType("TSMethodSignature", { + aliases: ["TSTypeElement"], + visitor: ["key", "typeParameters", "parameters", "typeAnnotation"], + fields: { + ...signatureDeclarationCommon, + ...namedTypeElementCommon, + }, +}); + +defineType("TSIndexSignature", { + aliases: ["TSTypeElement"], + visitor: ["parameters", "typeAnnotation"], + fields: { + readonly: validateOptional(bool), + parameters: validateArrayOfType("Identifier"), // Length must be 1 + typeAnnotation: validateOptionalType("TypeAnnotation"), + }, +}); + +const tsKeywordTypes = [ + "TSAnyKeyword", + "TSNumberKeyword", + "TSObjectKeyword", + "TSBooleanKeyword", + "TSStringKeyword", + "TSSymbolKeyword", + "TSVoidKeyword", + "TSUndefinedKeyword", + "TSNullKeyword", + "TSNeverKeyword", +]; + +for (const type of tsKeywordTypes) { + defineType(type, { + aliases: ["TSType"], + visitor: [], + fields: {}, + }); +} + +defineType("TSThisType", { + aliases: ["TSType"], + visitor: [], + fields: {}, +}); + +const fnOrCtr = { + aliases: ["TSType"], + visitor: ["typeParameters", "typeAnnotation"], + fields: signatureDeclarationCommon, +}; + +defineType("TSFunctionType", fnOrCtr); +defineType("TSConstructorType", fnOrCtr); + +defineType("TSTypeReference", { + aliases: ["TSType"], + visitor: ["typeName", "typeParameters"], + fields: { + typeName: validateType("TSEntityName"), + typeParameters: validateOptionalType("TypeParameterInstantiation"), + }, +}); + +defineType("TSTypePredicate", { + aliases: ["TSType"], + visitor: ["parameterName", "typeAnnotation"], + fields: { + parameterName: validateType(["Identifier", "TSThisType"]), + typeAnnotation: validateType("TypeAnnotation"), + }, +}); + +defineType("TSTypeQuery", { + aliases: ["TSType"], + visitor: ["exprName"], + fields: { + exprName: validateType("TSEntityName"), + }, +}); + +defineType("TSTypeLiteral", { + aliases: ["TSType"], + visitor: ["members"], + fields: { + members: validateArrayOfType("TSTypeElement"), + }, +}); + +defineType("TSArrayType", { + aliases: ["TSType"], + visitor: ["elementType"], + fields: { + elementType: validateType("TSType"), + }, +}); + +defineType("TSTupleType", { + aliases: ["TSType"], + visitor: ["elementTypes"], + fields: { + elementTypes: validateArrayOfType("TSType"), + }, +}); + +const unionOrIntersection = { + aliases: ["TSType"], + visitor: ["types"], + fields: { + types: validateArrayOfType("TSType"), + }, +}; + +defineType("TSUnionType", unionOrIntersection); +defineType("TSIntersectionType", unionOrIntersection); + +defineType("TSParenthesizedType", { + aliases: ["TSType"], + visitor: ["typeAnnotation"], + fields: { + typeAnnotation: validateType("TSType"), + }, +}); + +defineType("TSTypeOperator", { + aliases: ["TSType"], + visitor: ["typeAnnotation"], + fields: { + operator: validate(assertValueType("string")), + typeAnnotation: validateType("TSType"), + }, +}); + +defineType("TSIndexedAccessType", { + aliases: ["TSType"], + visitor: ["objectType", "indexType"], + fields: { + objectType: validateType("TSType"), + indexType: validateType("TSType"), + }, +}); + +defineType("TSMappedType", { + aliases: ["TSType"], + visitor: ["typeParameter", "typeAnnotation"], + fields: { + readonly: validateOptional(bool), + typeParameter: validateType("TypeParameter"), + optional: validateOptional(bool), + typeAnnotation: validateOptionalType("TSType"), + }, +}); + +defineType("TSLiteralType", { + aliases: ["TSType"], + visitor: ["literal"], + fields: { + literal: validateType([ + "NumericLiteral", + "StringLiteral", + "BooleanLiteral", + ]), + }, +}); + +defineType("TSExpressionWithTypeArguments", { + aliases: ["TSType"], + visitor: ["expression", "typeParameters"], + fields: { + expression: validateType("TSEntityName"), + typeParameters: validateOptionalType("TypeParameterInstantiation"), + }, +}); + +defineType("TSInterfaceDeclaration", { + // "Statement" alias prevents a semicolon from appearing after it in an export declaration. + aliases: ["Statement", "Declaration"], + visitor: ["id", "typeParameters", "extends", "body"], + fields: { + declare: validateOptional(bool), + id: validateType("Identifier"), + typeParameters: validateOptionalType("TypeParameterDeclaration"), + extends: validateOptional(arrayOfType("TSExpressionWithTypeArguments")), + body: validateType("TSInterfaceBody"), + }, +}); + +defineType("TSInterfaceBody", { + visitor: ["body"], + fields: { + body: validateArrayOfType("TSTypeElement"), + }, +}); + +defineType("TSTypeAliasDeclaration", { + aliases: ["Statement", "Declaration"], + visitor: ["id", "typeParameters", "typeAnnotation"], + fields: { + declare: validateOptional(bool), + id: validateType("Identifier"), + typeParameters: validateOptionalType("TypeParameterDeclaration"), + typeAnnotation: validateType("TSType"), + }, +}); + +defineType("TSAsExpression", { + aliases: ["Expression"], + visitor: ["expression", "typeAnnotation"], + fields: { + expression: validateType("Expression"), + typeAnnotation: validateType("TSType"), + }, +}); + +defineType("TSTypeAssertion", { + aliases: ["Expression"], + visitor: ["typeAnnotation", "expression"], + fields: { + typeAnnotation: validateType("TSType"), + expression: validateType("Expression"), + }, +}); + +defineType("TSEnumDeclaration", { + // "Statement" alias prevents a semicolon from appearing after it in an export declaration. + aliases: ["Statement", "Declaration"], + visitor: ["id", "members"], + fields: { + declare: validateOptional(bool), + const: validateOptional(bool), + id: validateType("Identifier"), + members: validateArrayOfType("TSEnumMember"), + initializer: validateOptionalType("Expression"), + }, +}); + +defineType("TSEnumMember", { + visitor: ["id", "initializer"], + fields: { + id: validateType(["Identifier", "StringLiteral"]), + initializer: validateOptionalType("Expression"), + }, +}); + +defineType("TSModuleDeclaration", { + aliases: ["Statement", "Declaration"], + visitor: ["id", "body"], + fields: { + declare: validateOptional(bool), + global: validateOptional(bool), + id: validateType(["Identifier", "StringLiteral"]), + body: validateType(["TSModuleBlock", "TSModuleDeclaration"]), + }, +}); + +defineType("TSModuleBlock", { + visitor: ["body"], + fields: { + body: validateArrayOfType("Statement"), + }, +}); + +defineType("TSImportEqualsDeclaration", { + aliases: ["Statement"], + visitor: ["id", "moduleReference"], + fields: { + isExport: validate(bool), + id: validateType("Identifier"), + moduleReference: validateType([ + "TSEntityName", + "TSExternalModuleReference", + ]), + }, +}); + +defineType("TSExternalModuleReference", { + visitor: ["expression"], + fields: { + expression: validateType("StringLiteral"), + }, +}); + +defineType("TSNonNullExpression", { + aliases: ["Expression"], + visitor: ["expression"], + fields: { + expression: validateType("Expression"), + }, +}); + +defineType("TSExportAssignment", { + aliases: ["Statement"], + visitor: ["expression"], + fields: { + expression: validateType("Expression"), + }, +}); + +defineType("TSNamespaceExportDeclaration", { + aliases: ["Statement"], + visitor: ["id"], + fields: { + id: validateType("Identifier"), + }, +});