diff --git a/packages/babel-types/src/ast-types/generated/index.ts b/packages/babel-types/src/ast-types/generated/index.ts index 3c4cdb3c6dcf..531a113769ee 100755 --- a/packages/babel-types/src/ast-types/generated/index.ts +++ b/packages/babel-types/src/ast-types/generated/index.ts @@ -1905,7 +1905,12 @@ export interface TSMappedType extends BaseNode { export interface TSLiteralType extends BaseNode { type: "TSLiteralType"; - literal: NumericLiteral | StringLiteral | BooleanLiteral | BigIntLiteral; + literal: + | NumericLiteral + | StringLiteral + | BooleanLiteral + | BigIntLiteral + | UnaryExpression; } export interface TSExpressionWithTypeArguments extends BaseNode { diff --git a/packages/babel-types/src/builders/generated/index.ts b/packages/babel-types/src/builders/generated/index.ts index 34d63d3ff278..658a503298ba 100755 --- a/packages/babel-types/src/builders/generated/index.ts +++ b/packages/babel-types/src/builders/generated/index.ts @@ -1328,7 +1328,8 @@ export function tsLiteralType( | t.NumericLiteral | t.StringLiteral | t.BooleanLiteral - | t.BigIntLiteral, + | t.BigIntLiteral + | t.UnaryExpression, ): t.TSLiteralType { return builder("TSLiteralType", ...arguments); } diff --git a/packages/babel-types/src/definitions/typescript.ts b/packages/babel-types/src/definitions/typescript.ts index 2cb997a440dd..7b4c34f44b60 100644 --- a/packages/babel-types/src/definitions/typescript.ts +++ b/packages/babel-types/src/definitions/typescript.ts @@ -15,6 +15,7 @@ import { functionDeclarationCommon, classMethodOrDeclareMethodCommon, } from "./core"; +import is from "../validators/is"; const bool = assertValueType("boolean"); @@ -335,12 +336,43 @@ defineType("TSLiteralType", { aliases: ["TSType", "TSBaseType"], visitor: ["literal"], fields: { - literal: validateType([ - "NumericLiteral", - "StringLiteral", - "BooleanLiteral", - "BigIntLiteral", - ]), + literal: { + validate: (function () { + const unaryExpression = assertNodeType( + "NumericLiteral", + "BigIntLiteral", + ); + const unaryOperator = assertOneOf("-"); + + const literal = assertNodeType( + "NumericLiteral", + "StringLiteral", + "BooleanLiteral", + "BigIntLiteral", + ); + function validator(parent, key: string, node) { + // type A = -1 | 1; + if (is("UnaryExpression", node)) { + // check operator first + unaryOperator(node, "operator", node.operator); + unaryExpression(node, "argument", node.argument); + } else { + // type A = 'foo' | 'bar' | false | 1; + literal(parent, key, node); + } + } + + validator.oneOfNodeTypes = [ + "NumericLiteral", + "StringLiteral", + "BooleanLiteral", + "BigIntLiteral", + "UnaryExpression", + ]; + + return validator; + })(), + }, }, }); diff --git a/packages/babel-types/test/builders/typescript/__snapshots__/tsLiteralType.js.snap b/packages/babel-types/test/builders/typescript/__snapshots__/tsLiteralType.js.snap new file mode 100644 index 000000000000..ed9a92b86e4c --- /dev/null +++ b/packages/babel-types/test/builders/typescript/__snapshots__/tsLiteralType.js.snap @@ -0,0 +1,31 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`builders typescript tsLiteralType accept unary expression 1`] = ` +Object { + "literal": Object { + "argument": Object { + "type": "NumericLiteral", + "value": 1, + }, + "operator": "-", + "prefix": true, + "type": "UnaryExpression", + }, + "type": "TSLiteralType", +} +`; + +exports[`builders typescript tsLiteralType accept unary expression 2`] = ` +Object { + "literal": Object { + "argument": Object { + "type": "BigIntLiteral", + "value": "123456789", + }, + "operator": "-", + "prefix": true, + "type": "UnaryExpression", + }, + "type": "TSLiteralType", +} +`; diff --git a/packages/babel-types/test/builders/typescript/tsLiteralType.js b/packages/babel-types/test/builders/typescript/tsLiteralType.js new file mode 100644 index 000000000000..298b58563f06 --- /dev/null +++ b/packages/babel-types/test/builders/typescript/tsLiteralType.js @@ -0,0 +1,52 @@ +import * as t from "../../.."; + +describe("builders", function () { + describe("typescript", function () { + describe("tsLiteralType", function () { + it("accept unary expression", function () { + expect( + t.tsLiteralType(t.unaryExpression("-", t.numericLiteral(1))), + ).toMatchSnapshot(); + expect( + t.tsLiteralType(t.unaryExpression("-", t.bigIntLiteral("123456789"))), + ).toMatchSnapshot(); + }); + it("throws with non-numeric argument", function () { + expect(() => { + t.tsLiteralType(t.unaryExpression("-", t.stringLiteral(1))); + }).toThrow("Property value expected type of string but got number"); + expect(() => { + t.tsLiteralType(t.unaryExpression("-", t.objectExpression([]))); + }).toThrow( + 'Property argument of UnaryExpression expected node to be of a type ["NumericLiteral","BigIntLiteral"] but instead got "ObjectExpression"', + ); + }); + }); + it("throws with bad operator", function () { + expect(() => { + t.tsLiteralType(t.unaryExpression("+", t.numericLiteral(1))); + }).toThrow( + 'Property operator expected value to be one of ["-"] but got "+"', + ); + + // should check operator first since it appears first + expect(() => { + t.tsLiteralType(t.unaryExpression("+", t.objectExpression([]))); + }).toThrow( + 'Property operator expected value to be one of ["-"] but got "+"', + ); + + expect(() => { + t.tsLiteralType(t.unaryExpression("~", t.numericLiteral(1))); + }).toThrow( + 'Property operator expected value to be one of ["-"] but got "~"', + ); + + expect(() => { + t.tsLiteralType(t.unaryExpression("void", t.numericLiteral(1))); + }).toThrow( + 'Property operator expected value to be one of ["-"] but got "void"', + ); + }); + }); +});