# Type declaration {"dialect": "ts"}

function foo(a: number, b: "literal" | Map<number, boolean>): RegExp[] {}

==>

Script(FunctionDeclaration(function, VariableDefinition, ParamList(
  VariableDefinition, TypeAnnotation(TypeName),
  VariableDefinition, TypeAnnotation(UnionType(LiteralType(String), LogicOp, ParameterizedType(TypeName, TypeArgList(TypeName, TypeName))))
), TypeAnnotation(ArrayType(TypeName)), Block))

# Type predicate {"dialect": "ts"}

function isFoo(foo: any): foo is Foo { return true }

==>

Script(FunctionDeclaration(function, VariableDefinition, ParamList(
  VariableDefinition, TypeAnnotation(TypeName)
), TypePredicate(VariableName, is, TypeName), Block(ReturnStatement(return, BooleanLiteral))))

# Type alias {"dialect": "ts"}

type Foo<T extends string> = T[]

==>

Script(TypeAliasDeclaration(type, TypeDefinition, TypeParamList(TypeDefinition, extends, TypeName), Equals, ArrayType(TypeName)))

# Enum declaration {"dialect": "ts"}

const enum Type { Red = 1, Blue, Green }

==>

Script(EnumDeclaration(const, enum, TypeDefinition, EnumBody(PropertyName, Equals, Number, PropertyName, PropertyName)))

# Interface declaration {"dialect": "ts"}

interface Foo {
  readonly a: number
  b(arg: string): void
  (call: number): boolean
  new (): Foo
  readonly [x: string]: number
}

==>

Script(InterfaceDeclaration(interface, TypeDefinition, ObjectType(
  PropertyType(readonly, PropertyDefinition, TypeAnnotation(TypeName)),
  MethodType(PropertyDefinition, ParamList(VariableDefinition, TypeAnnotation(TypeName)), TypeAnnotation(VoidType(void))),
  CallSignature(ParamList(VariableDefinition, TypeAnnotation(TypeName)), TypeAnnotation(TypeName)),
  NewSignature(new,ParamList, TypeAnnotation(TypeName)),
  IndexSignature(readonly, PropertyDefinition, TypeAnnotation(TypeName), TypeAnnotation(TypeName)))))

# Call type args {"dialect": "ts"}

foo<number, string>() + new Bar<11>()
x < 10 > 5

==>

Script(
  ExpressionStatement(BinaryExpression(
    CallExpression(VariableName, TypeArgList(TypeName, TypeName), ArgList),
    ArithOp,
    NewExpression(new, VariableName, TypeArgList(LiteralType(Number)), ArgList))),
  ExpressionStatement(BinaryExpression(BinaryExpression(VariableName, CompareOp, Number), CompareOp, Number)))

# Advanced types {"dialect": "ts"}

let x: typeof X.x | keyof Y & Z["Foo"] | A<string>
let tuple: [a, b]
let f: (x: number) => boolean

==>

Script(
  VariableDeclaration(let, VariableDefinition, TypeAnnotation(
    UnionType(UnionType(TypeofType(typeof, MemberExpression(VariableName, PropertyName)), LogicOp,
                        IntersectionType(KeyofType(keyof, TypeName), LogicOp, IndexedType(TypeName, String))),
              LogicOp, ParameterizedType(TypeName, TypeArgList(TypeName))))),
  VariableDeclaration(let, VariableDefinition, TypeAnnotation(TupleType(TypeName, TypeName))),
  VariableDeclaration(let, VariableDefinition, TypeAnnotation(FunctionSignature(
    ParamList(VariableDefinition, TypeAnnotation(TypeName)), Arrow, TypeName))))

# Prefix cast {"dialect": "ts"}

<string>foo

==>

Script(ExpressionStatement(PrefixCast(TypeName, VariableName)))

# No prefix cast in JSX {"dialect": "ts jsx"}

<string>foo</string>

==>

Script(ExpressionStatement(JSXElement(
  JSXOpenTag(JSXStartTag, JSXIdentifier, JSXEndTag),
  JSXText,
  JSXCloseTag(JSXStartCloseTag, JSXIdentifier, JSXEndTag))))

# Class definition {"dialect": "ts"}

class Foo<T> extends Bar<T> implements Stuff {
  a: number
  public readonly b: string = "two"
  constructor(readonly x: boolean, public y: number, z: string) {}
  private static blah(): void {}
}

==>

Script(ClassDeclaration(
  class, VariableDefinition, TypeParamList(TypeDefinition),
  extends ParameterizedType(TypeName, TypeArgList(TypeName)),
  implements TypeName,
  ClassBody(
    PropertyDeclaration(PropertyDefinition, TypeAnnotation(TypeName)),
    PropertyDeclaration(Privacy, readonly, PropertyDefinition, TypeAnnotation(TypeName), Equals, String),
    MethodDeclaration(PropertyDefinition, ParamList(
      readonly, VariableDefinition, TypeAnnotation(TypeName),
      Privacy, VariableDefinition, TypeAnnotation(TypeName),
      VariableDefinition, TypeAnnotation(TypeName)), Block),
    MethodDeclaration(Privacy, static, PropertyDefinition, ParamList, TypeAnnotation(VoidType(void)), Block))))

# Arrow with type params {"dialect": "ts"}

let x = <T>(arg: T): T => arg

==>

Script(VariableDeclaration(let, VariableDefinition, Equals, ArrowFunction(
  TypeParamList(TypeDefinition),
  ParamList(VariableDefinition, TypeAnnotation(TypeName)),
  TypeAnnotation(TypeName),
  Arrow,
  VariableName)))

# Template types {"dialect": "ts"}

type Tmpl<T> = `${string} ${5}` | `one ${Two}`

==>

Script(TypeAliasDeclaration(type, TypeDefinition, TypeParamList(TypeDefinition), Equals,
  UnionType(TemplateType(Interpolation(InterpolationStart,TypeName,InterpolationEnd), Interpolation(InterpolationStart,LiteralType(Number),InterpolationEnd)), LogicOp, TemplateType(Interpolation(InterpolationStart,TypeName,InterpolationEnd)))))

# Extending complex types {"dialect": "ts"}

class Foo extends A.B<Param> {}

==>

Script(ClassDeclaration(class, VariableDefinition,
  extends, ParameterizedType(IndexedType(TypeName, TypeName), TypeArgList(TypeName)),
  ClassBody))
