Browse Source

* typescript parsing & conversion, initial commit

Michaël Van Canneyt 3 years ago
parent
commit
65101b36dc

File diff suppressed because it is too large
+ 607 - 79
packages/fcl-js/src/jsparser.pp


+ 19 - 1
packages/fcl-js/src/jsscanner.pp

@@ -22,7 +22,7 @@ interface
 uses SysUtils, Classes, jstoken;
 
 Type
-  TECMAVersion = (ecma5,ecma2021);
+  TECMAVersion = (ecma5,ecma2015,ecma2021);
 
 Const
   // TJSToken is the maximum known set of keywords.
@@ -30,6 +30,7 @@ Const
   NonJSKeywords : Array [TECMAVersion] of TJSTokens
     = (
         [tjsAwait, tjsClass, tjsConst,tjsDebugger,tjsEnum, tjsExport, tjsExtends, tjsImport, tjsLet, tjsSUPER, tjsYield], //ecma5
+        [], // ecma2015
         [] // ecma2022
       );
 
@@ -84,6 +85,7 @@ Type
   TJSScanner = class
   private
     FECMAVersion: TECMAVersion;
+    FIsTypeScript: Boolean;
     FReturnComments: Boolean;
     FReturnWhiteSpace: Boolean;
     FSourceFile: TLineReader;
@@ -109,6 +111,7 @@ Type
     function ReadUnicodeEscape: WideChar;
     Function ReadRegex : TJSToken;
     procedure SetECMAVersion(AValue: TECMAVersion);
+    procedure SetIsTypeScript(AValue: Boolean);
   protected
     procedure Error(const Msg: string);overload;
     procedure Error(const Msg: string; Args: array of Const);overload;
@@ -131,6 +134,7 @@ Type
     property CurToken: TJSToken read FCurToken;
     property CurTokenString: string read FCurTokenString;
     property ECMAVersion : TECMAVersion Read FECMAVersion Write SetECMAVersion;
+    Property IsTypeScript : Boolean Read FIsTypeScript Write SetIsTypeScript;
   end;
 
 
@@ -167,6 +171,7 @@ begin
   inherited Create;
   FSourceFile := ALineReader;
   ECMAVersion:=aECMAVersion;
+  FNonKeyWords:=NonJSKeywords[aECMAVersion];
 end;
 
 constructor TJSScanner.Create(AStream: TStream; aECMAVersion: TECMAVersion);
@@ -395,6 +400,14 @@ begin
   FNonKeyWords:=NonJSKeywords[aValue];
 end;
 
+procedure TJSScanner.SetIsTypeScript(AValue: Boolean);
+begin
+  if FIsTypeScript=AValue then Exit;
+  FIsTypeScript:=AValue;
+  if True then
+    ecmaversion:=ecma2021;
+end;
+
 function TJSScanner.DoStringLiteral: TJSToken;
 
 Var
@@ -752,6 +765,11 @@ begin
         else
           Result:=tjsEQ;
         end
+      else if (TokenStr[0]='>') then
+        begin
+        Inc(TokenStr);
+        Result:=tjsArrow;
+        end
       else
         Result := tjsAssign;
       end;

+ 2 - 2
packages/fcl-js/src/jstoken.pp

@@ -36,7 +36,7 @@ type
      tjsPLUSEQ, tjsPLUSPLUS,
      tjsURSHIFT, tjsURSHIFTEQ,
      tjsRSHIFT, tjsRSHIFTEQ,
-     tjsSEQ, tjsSNE, tjsMULEQ,
+     tjsSEQ, tjsSNE, tjsMULEQ, tjsArrow,
      { Reserved words start here. They must be last }
      tjsAWAIT, tjsBREAK, tjsCASE, tjsCATCH, tjsCLASS, tjsCONST, tjsCONTINUE,
      tjsDEBUGGER, tjsDEFAULT, tjsDELETE, tjsDO,
@@ -74,7 +74,7 @@ const
         '+=', '++',
         '>>>', '>>>=',
         '>>', '>>=',
-        '===', '!==', '*=',
+        '===', '!==', '*=', '=>',
         // Identifiers last
         'await', 'break','case','catch', 'class','const','continue',
      'debugger','default','delete', 'do',

+ 709 - 19
packages/fcl-js/src/jstree.pp

@@ -81,7 +81,7 @@ Type
   TJSElementFlags = set of TJSElementFlag;
 
   TJSFunctionBody = Class;
-  TJSClassDeclaration = Class;
+
 
   { TJSElement }
 
@@ -109,10 +109,10 @@ Type
     FLocationLine: Integer;
     FLocationPos: Integer;
     FLocationSource: String;
-    FName: String;
+    FName: jsBase.TJSString;
     FNext: TJSLabel;
   Public
-    Property Name : String Read FName Write FName;
+    Property Name : jsBase.TJSString Read FName Write FName;
     Property LabelSet : TJSLabelSet Read FLabelSet Write FLabelSet;
     Property LocationSource : String Read FLocationSource Write FLocationSource;
     Property LocationLine : Integer Read FLocationLine Write FLocationLine;
@@ -123,6 +123,9 @@ Type
   TJSString = jsbase.TJSString; // beware of jstoken.tjsString
 
   { TJSFuncDef - part of TJSFunctionDeclarationStatement, e.g. 'function Name(Params)Body' }
+  TJSTypedParams = Class;
+  TJSTypeDef = Class;
+  TJSEnumTypeDef = Class;
 
   TJSFuncDef = Class(TJSObject)
   private
@@ -131,11 +134,16 @@ Type
     FIsEmpty: Boolean;
     FName: TJSString;
     FParams: TStrings;
+    FResultType: TJSTypeDef;
+    FTypedParams: TJSTypedParams;
     procedure SetParams(const AValue: TStrings);
   Public
     Constructor Create;
     Destructor Destroy; override;
-    Property Params : TStrings Read FParams Write SetParams;
+    Procedure UpdateParams;
+    Property TypedParams : TJSTypedParams Read FTypedParams;
+    Property ResultType : TJSTypeDef Read FResultType Write FResultType;
+    Property Params : TStrings Read FParams; deprecated;
     Property Body : TJSFunctionBody Read FBody Write FBody; // can be nil
     Property Name : TJSString Read FName Write FName;
     Property IsEmpty : Boolean Read FIsEmpty Write FIsEmpty;
@@ -146,6 +154,7 @@ Type
 
   TJSElement = Class(TJSObject)
   private
+    FData: TObject;
     FFlags: TJSElementFlags;
     FLine: Integer;
     FColumn: Integer;
@@ -157,6 +166,7 @@ Type
     Property Line : Integer Read FLine Write FLine;
     Property Column : Integer Read FColumn Write FColumn;
     Property Flags : TJSElementFlags Read FFlags Write FFlags;
+    Property Data : TObject Read FData write FData;
   end;
   TJSElementClass = Class of TJSElement;
   TJSElementArray = array of TJSElement;
@@ -361,6 +371,7 @@ Type
     Property VarType : TJSVarType Read FVarType Write FVarType;
   end;
 
+
   { TJSExpressionStatement - A; }
 
   TJSExpressionStatement = Class(TJSUnary);
@@ -703,6 +714,15 @@ Type
     Class function OperatorToken : tjsToken; override;
   end;
 
+  { LHS => Expr }
+
+  { TJSArrowFunction }
+
+  TJSArrowFunction = Class(TJSAssignStatement)
+  Public
+    Class function OperatorToken : tjsToken; override;
+  end;
+
   { TJSMulEqAssignStatement - e.g. LHS*=Expr }
 
   TJSMulEqAssignStatement = Class(TJSAssignStatement)
@@ -785,13 +805,21 @@ Type
   TJSVarDeclaration = Class(TJSElement)
   private
     FInit: TJSElement;
-    FName: String;
+    FName: TJSString;
+    FOwnsType: Boolean;
+    FTyped: TJSTypeDef;
     FVarType: TJSVarType;
+    procedure SetTyped(AValue: TJSTypeDef);
   Public
     Destructor Destroy; override;
-    Property Name : String Read FName Write FName;
+    procedure SetForeignType(AValue: TJSTypeDef);
+    Property Name : TJSString Read FName Write FName;
     Property Init : TJSElement Read FInit Write FInit;
+    // let, var, const
     Property VarType : TJSVarType Read FVarType Write FVarType;
+    // Typescript type. Setting to non-nil value will set OwnsType
+    Property Typed : TJSTypeDef Read FTyped Write SetTyped;
+    Property OwnsType : Boolean Read FOwnsType;
   end;
 
   { TJSIfStatement - e.g. if (Cond) btrue else bfalse }
@@ -1053,9 +1081,11 @@ Type
   TJSFunctionDeclarationStatement = Class(TJSElement)
   private
     FFuncDef: TJSFuncDef;
+    FIsGenerator: Boolean;
   Public
     Destructor Destroy; override;
     Property AFunction : TJSFuncDef Read FFuncDef Write FFuncDef;
+    Property IsGenerator : Boolean Read FIsGenerator Write FIsGenerator;
   end;
 
   { TJSFunctionBody - the statement block of a function }
@@ -1067,49 +1097,627 @@ Type
     Property isProgram : Boolean Read FIsProgram Write FIsProgram;
   end;
 
-  TJSClassDeclaration = Class(TJSElement)
-  end;
-
 
   { TJSElementNode - element of TJSElementNodes }
 
   TJSElementNode = Class(TCollectionItem)
   private
+    FIsAmbient: Boolean;
+    FIsExport: Boolean;
     FNode: TJSElement;
   Public
     Destructor Destroy; override;
     Property Node : TJSElement Read FNode Write FNode;
+    Property IsAmbient : Boolean Read FIsAmbient Write FIsAmbient;
+    Property IsExport : Boolean Read FIsExport Write FIsExport;
   end;
 
   { TJSElementNodes - see TJSSourceElements }
 
   TJSElementNodes = Class(TCollection)
   private
+    FClearNodes: Boolean;
+    FNodeType: String;
     function GetN(AIndex : Integer): TJSElementNode;
   Public
-    Function AddNode : TJSElementNode;
+    Destructor Destroy; override;
+    Procedure ClearNodes;
+    Function AddNode(aIsAmbient : Boolean = False; aIsExport : Boolean = False) : TJSElementNode;
+    Function AddNode(aEl : TJSElement; aIsAmbient : Boolean = False; aIsExport : Boolean = False) : TJSElementNode;
     Function InsertNode(Index: integer) : TJSElementNode;
     Property Nodes[AIndex : Integer] : TJSElementNode Read GetN ; default;
+    Property NodeType : String Read FNodeType Write FNodeType;
+    Property DoClearNodes : Boolean Read FClearNodes Write FClearNodes;
+  end;
+
+  { TJSTypedParam }
+
+  TJSTypedParam = Class(TJSElementNode)
+  private
+    FName: jsbase.TJSString;
+  Public
+    Property Name : jsbase.TJSString Read FName Write FName;
+  end;
+
+  { TJSTypedParams }
+
+  TJSTypedParams = class(TJSElementNodes)
+  private
+    function GetNames(aIndex : Integer): String;
+    function GetParams(aIndex : Integer): TJSTypedParam;
+    function GetTypes(aIndex : Integer): TJSElement;
+  Public
+    function AddParam(aName : jsBase.TJSString) : TJSTypedParam;
+    Property Params[aIndex : Integer] : TJSTypedParam Read GetParams;
+    Property Types[aIndex : Integer] : TJSElement Read GetTypes;
+    Property Names[aIndex : Integer] : String Read GetNames; default;
   end;
 
+
   { TJSSourceElements - a list of elements, every element ends in semicolon,
     first Vars, then Functions, finally Statements }
 
   TJSSourceElements = Class(TJSElement)
   private
     FFunctions: TJSElementNodes;
+    FModules: TJSElementNodes;
+    FNamespaces: TJSElementNodes;
     FStatements: TJSElementNodes;
+    FInterfaces : TJSElementNodes;
+    FTypes: TJSElementNodes;
+    FEnums: TJSElementNodes;
     FVars: TJSElementNodes;
+    FClasses: TJSElementNodes;
   Public
     Constructor Create(ALine,AColumn : Integer; const ASource : String = ''); override;
     Destructor Destroy; override;
     Property Vars : TJSElementNodes Read FVars;
     Property Functions : TJSElementNodes Read FFunctions;
     Property Statements : TJSElementNodes Read FStatements;
+    Property Classes : TJSElementNodes Read FClasses;
+    Property Modules : TJSElementNodes Read FModules;
+    Property Types : TJSElementNodes Read FTypes;
+    Property Interfaces : TJSElementNodes Read FInterfaces;
+    Property Enums : TJSElementNodes Read FEnums;
+    Property Namespaces : TJSElementNodes Read FNamespaces;
+  end;
+
+  { TJSFunctionBody - the statement block of a function }
+
+  TJSNamedElement = class(TJSElement)
+  Private
+    FName: TJSString;
+  Public
+    Property Name : TJSString Read FName Write FName;
+  end;
+
+  TJSTypeDef = class(TJSElement);
+
+
+  { TJSTypeReference }
+
+  TJSTypeReference = class(TJSTypeDef)
+  Private
+    FIsTypeOf: Boolean;
+    FName: TJSString;
+  Public
+    Property Name : TJSString Read FName Write FName;
+    Property IsTypeOf : Boolean Read FIsTypeOf Write FIsTypeOf;
+  end;
+
+
+  { TJSStructuredTypeDef }
+
+  TJSStructuredTypeDef = class(TJSTypeDef)
+  Private
+    FValues: TJSElementNodes;
+  Public
+    Constructor Create(ALine, AColumn: Integer; const ASource: String=''); override;
+    Destructor destroy; override;
+    procedure AddValue(aElement : TJSElement); virtual;
+    Property Values : TJSElementNodes Read FValues;
+  end;
+
+  { TJSEnumTypeDef }
+
+  TJSEnumTypeDef = class(TJSStructuredTypeDef)
+  private
+    function GetName(aIndex : Integer): jsBase.TJSString;
+    function GetNameCount: Integer;
+  Public
+    // names are TJSliteral
+    Procedure AddName(aName : String);
+    Property NameCount : Integer Read GetNameCount;
+    Property Names[aIndex : Integer] : jsBase.TJSString Read GetName;
+  end;
+  { TJSArrowFunctionTypeDef }
+
+  { TJSNamedTypedElement }
+
+  TJSNamedTypedElement = class(TJSNamedElement)
+  private
+    FElementType: TJSTypeDef;
+  Public
+    Destructor Destroy; override;
+    Property ElementType : TJSTypeDef Read FElementType Write FElementType;
+  end;
+
+  { TJSFunctionParamDef }
+
+
+
+  TJSFunctionParamDef = class(TJSNamedTypedElement)
+  private
+    function GetParamType: TJSTypeDef;
+    procedure SetParamType(AValue: TJSTypeDef);
+  Public
+    Property ParamType : TJSTypeDef Read GetParamType Write SetParamType;
+  end;
+
+  TJSArrowFunctionTypeDef = class(TJSTypeDef) // Params are in values.
+  private
+    FFunction: TJSFuncDef;
+  Public
+    Constructor Create(ALine,AColumn : Integer; Const ASource : String = ''); override;
+    Destructor Destroy; override;
+    Property aFunction : TJSFuncDef Read FFunction Write FFunction;
+  end;
+
+  { TJSUnionTypeDef }
+
+  TJSUnionOrTupleTypeDef = Class(TJSStructuredTypeDef)
+  private
+    function GetType(aIndex : Integer): TJSTypeDef;
+    function GetTypeCount: Integer;
+  Public
+    Property Types[aIndex : Integer] : TJSTypeDef Read GetType;
+    property TypeCount : Integer Read GetTypeCount;
+  end;
+  TJSUnionTypeDef = class(TJSUnionOrTupleTypeDef);
+  TJSTupleTypeDef = class(TJSUnionOrTupleTypeDef);
+  TJSInterSectionTypeDef = class(TJSUnionOrTupleTypeDef);
+
+  { TJSArrayTypeDef }
+
+  TJSArrayTypeDef = Class(TJSTypeDef)
+  private
+    FBaseType: TJSTypeDef;
+  Public
+    Destructor Destroy; override;
+    Property BaseType : TJSTypeDef Read FBaseType Write FBaseType;
+  end;
+
+
+  { TJSGenericTypeRef }
+
+  TJSGenericTypeRef = Class(TJSStructuredTypeDef)
+  private
+    FBaseType: TJSTypeDef;
+    function GetType(aIndex : Integer): TJSTypeDef;
+    function GetTypeCount: Integer;
+  Public
+    Destructor destroy; override;
+    Property BaseType : TJSTypeDef Read FBaseType Write FBaseType;
+    Property ParamTypes[aIndex : Integer] : TJSTypeDef Read GetType;
+    property ParamTypeCount : Integer Read GetTypeCount;
+  end;
+
+  { TJSObjectTypeElementDef }
+
+  TJSObjectTypeElementDef = Class(TJSNamedTypedElement)
+  private
+    FOptional: Boolean;
+  Public
+    Destructor Destroy; override;
+    Property Optional : Boolean Read FOptional Write FOptional;
+  end;
+
+  TJSPropertyDeclaration = Class(TJSObjectTypeElementDef);
+
+  { TJSMethodDeclaration }
+
+  TJSMethodDeclaration = Class(TJSObjectTypeElementDef)
+  private
+    FFuncDef: TJSFuncDef;
+    FTypeParams: TJSElementNodes;
+  Public
+    Destructor Destroy; override;
+    Property TypeParams : TJSElementNodes Read FTypeParams Write FTypeParams;
+    Property FuncDef : TJSFuncDef Read FFuncDef Write FFuncDef;
+  end;
+
+  { TJSObjectTypeDef }
+
+  TJSObjectTypeDef = Class(TJSStructuredTypeDef)
+  private
+    FName: TJSString;
+    function GetElement(aIndex : Integer): TJSObjectTypeElementDef;
+    function GetElementCount: Integer;
+  Public
+    procedure AddElement(const aEl: TJSObjectTypeElementDef);
+    Property Name : TJSString Read FName Write FName;
+    Property Elements[aIndex : Integer] : TJSObjectTypeElementDef Read GetElement;
+    Property ElementCount : Integer Read GetElementCount;
+  end;
+
+
+  { TJSTypeDeclaration }
+
+  TJSTypeDeclaration = class(TJSNamedElement)
+  Private
+    FTypeDef : TJSTypeDef;
+    FTypeParams : TJSElementNodes;
+  Public
+    Destructor Destroy; override;
+    Property TypeParams : TJSElementNodes Read FTypeParams Write FTypeParams;
+    Property TypeDef : TJSTypeDef Read FTypeDef Write FTypeDef;
+  end;
+
+  { TJSTypeStatement }
+
+  TJSTypeStatement = Class(TJSElement)
+  private
+    FTypeDecl: TJSTypeDeclaration;
+  Public
+    Destructor Destroy; override;
+    Property TypeDecl : TJSTypeDeclaration Read FTypeDecl Write FTypeDecl;
+  end;
+
+
+  { TJSEnumDeclaration }
+
+  TJSEnumDeclaration = Class(TJSNamedElement)
+  private
+    FEnumDef: TJSEnumTypeDef;
+  Public
+    Destructor Destroy; override;
+    Property EnumDef : TJSEnumTypeDef Read FEnumDef Write FEnumDef;
+  end;
+
+  { TJSTypeStatement }
+
+  { TJSEnumStatement }
+
+  TJSEnumStatement = Class(TJSElement)
+  private
+    FEnumDecl : TJSEnumDeclaration;
+  Public
+    Destructor Destroy; override;
+    Property EnumDecl : TJSEnumDeclaration Read FEnumDecl Write FEnumDecl;
+  end;
+
+
+  { TJSClassDeclaration }
+
+  TJSMembersDeclaration = Class(TJSElement)
+  Private
+    FMembers: TJSSourceElements;
+    procedure SetMembers(AValue: TJSSourceElements);
+  Public
+    Destructor Destroy; override;
+    property Members : TJSSourceElements Read FMembers Write SetMembers;
+  end;
+
+  TJSNamedMembersDeclaration = Class(TJSMembersDeclaration)
+  Private
+    FName: TJSString;
+  Public
+    Property Name : TJSString Read FName Write FName;
+  end;
+
+  TJSClassDeclaration = Class(TJSNamedMembersDeclaration)
+  private
+    FExtends: TJSString;
+  Public
+    Property Extends : TJSString Read FExtends Write FExtends;
+  end;
+
+  { TJSClassStatement }
+
+  TJSClassStatement =  Class(TJSElement)
+  private
+    FDecl: TJSClassDeclaration;
+  Public
+    Destructor Destroy; override;
+    Property Decl : TJSClassDeclaration Read FDecl Write FDecl;
+  end;
+  { TJSInterfaceDeclaration }
+
+  TJSInterfaceDeclaration = Class(TJSObjectTypeDef)
+  private
+    FExtends: TJSElementNodes;
+  Public
+    Destructor Destroy; override;
+    Procedure AddExtends(Const aName : TJSString);
+    property Extends : TJSElementNodes Read FExtends;
+  end;
+
+
+
+  { TJSInterfaceStatement }
+
+  TJSInterfaceStatement = Class(TJSElement)
+  private
+    FDecl: TJSInterfaceDeclaration;
+  Public
+    Destructor Destroy; override;
+    Property Decl : TJSInterfaceDeclaration Read FDecl Write FDecl;
+  end;
+
+
+  { TJSModuleDeclaration }
+
+  TJSModuleDeclaration = Class(TJSNamedMembersDeclaration)
+  end;
+
+  TJSNamespaceDeclaration = Class(TJSNamedMembersDeclaration)
   end;
 
 implementation
 
+{ TJSClassStatement }
+
+destructor TJSClassStatement.Destroy;
+begin
+  FreeAndNil(FDecl);
+  inherited Destroy;
+end;
+
+{ TJSMethodDeclaration }
+
+destructor TJSMethodDeclaration.Destroy;
+begin
+  FreeAndNil(FTypeParams);
+  FreeAndNil(FFuncDef);
+  inherited Destroy;
+end;
+
+{ TJSNamedTypedElement }
+
+destructor TJSNamedTypedElement.Destroy;
+begin
+  FreeAndNil(FElementType);
+  inherited Destroy;
+end;
+
+{ TJSInterfaceDeclarationStatement }
+
+destructor TJSInterfaceStatement.Destroy;
+begin
+  FreeAndNil(FDecl);
+  inherited Destroy;
+end;
+
+{ TJSInterfaceDeclaration }
+
+destructor TJSInterfaceDeclaration.Destroy;
+begin
+  FreeAndNil(FExtends);
+  inherited Destroy;
+end;
+
+procedure TJSInterfaceDeclaration.AddExtends(const aName: TJSString);
+
+Var
+  Lit : TJSLiteral;
+
+begin
+  if FExtends=Nil then
+    FExtends:=TJSElementNodes.Create(TJSElementNode);
+  Lit:=TJSLiteral.Create(0,0,'');
+  Lit.Value:=TJSValue.Create(aName);
+  FExtends.AddNode().Node:=Lit
+end;
+
+{ TJSEnumStatement }
+
+destructor TJSEnumStatement.Destroy;
+begin
+  FreeAndNil(FEnumDecl);
+  inherited Destroy;
+end;
+
+{ TJSEnumTypeDef }
+
+function TJSEnumTypeDef.GetName(aIndex : Integer): jsBase.TJSString;
+begin
+  Result:=TJSLiteral(Values.Nodes[aIndex].Node).Value.AsString;
+end;
+
+function TJSEnumTypeDef.GetNameCount: Integer;
+begin
+  Result:=Values.Count;
+end;
+
+procedure TJSEnumTypeDef.AddName(aName: String);
+
+Var
+  Lit : TJSLiteral;
+
+begin
+  Lit:=TJSLiteral.Create(0,0,'');
+  Lit.Value:=TJSValue.Create(aName);
+  Values.AddNode().Node:=Lit
+end;
+
+{ TJSEnumDeclaration }
+
+destructor TJSEnumDeclaration.Destroy;
+
+begin
+  FreeAndNil(FEnumDef);
+  inherited Destroy;
+end;
+
+{ TJSFunctionParamDef }
+
+function TJSFunctionParamDef.GetParamType: TJSTypeDef;
+begin
+  Result:=ElementType;
+end;
+
+procedure TJSFunctionParamDef.SetParamType(AValue: TJSTypeDef);
+begin
+  ElementType:=ParamType;
+end;
+
+{ TJSArrowFunctionTypeDef }
+
+constructor TJSArrowFunctionTypeDef.Create(ALine, AColumn: Integer; const ASource: String);
+begin
+  inherited Create(ALine, AColumn, ASource);
+  FFunction:=TJSFuncDef.Create;
+end;
+
+destructor TJSArrowFunctionTypeDef.Destroy;
+begin
+  FreeAndNil(FFunction);
+  inherited Destroy;
+end;
+
+{ TJSArrowFunction }
+
+class function TJSArrowFunction.OperatorToken: tjsToken;
+begin
+  Result:=tjsArrow;
+end;
+
+{ TJSObjectTypeDef }
+
+function TJSObjectTypeDef.GetElement(aIndex : Integer): TJSObjectTypeElementDef;
+begin
+  Result:=Values[aIndex].Node as TJSObjectTypeElementDef;
+end;
+
+function TJSObjectTypeDef.GetElementCount: Integer;
+begin
+  Result:=Values.Count;
+end;
+
+Procedure TJSObjectTypeDef.AddElement(const aEl: TJSObjectTypeElementDef);
+begin
+  Values.AddNode(False).Node:=aEl;
+end;
+
+{ TJSObjectTypeElementDef }
+
+destructor TJSObjectTypeElementDef.Destroy;
+begin
+  FreeAndNil(FElementType);
+  inherited Destroy;
+end;
+
+{ TJSArrayTypeDef }
+
+destructor TJSArrayTypeDef.Destroy;
+begin
+  FreeAndNil(FBaseType);
+  inherited Destroy;
+end;
+
+{ TJSUnionTypeDef }
+
+function TJSUnionOrTupleTypeDef.GetType(aIndex : Integer): TJSTypeDef;
+begin
+  Result:=Values.Nodes[aIndex].Node as TJSTypeDef;
+end;
+
+function TJSUnionOrTupleTypeDef.GetTypeCount: Integer;
+begin
+  Result:=Values.Count;
+end;
+
+{ TJSTypeStatement }
+
+destructor TJSTypeStatement.Destroy;
+begin
+  FreeAndNil(FTypeDecl);
+  inherited Destroy;
+end;
+
+{ TJSTypeDeclaration }
+
+destructor TJSTypeDeclaration.Destroy;
+begin
+  Writeln('Destroying ',ClassName);
+  FreeAndNil(FTypeDef);
+  FreeAndNil(FTypeParams);
+  inherited Destroy;
+end;
+
+{ TJSGenericTypeRef }
+
+function TJSGenericTypeRef.GetType(aIndex : Integer): TJSTypeDef;
+begin
+  Result:=Values[aIndex].Node as TJSTypeDef;
+end;
+
+function TJSGenericTypeRef.GetTypeCount: Integer;
+begin
+  Result:=Values.Count;
+end;
+
+destructor TJSGenericTypeRef.destroy;
+begin
+  FreeAndNil(FBaseType);
+  inherited destroy;
+end;
+
+{ TJSStructuredTypeDef }
+
+constructor TJSStructuredTypeDef.Create(ALine, AColumn: Integer; const ASource: String);
+begin
+  inherited Create(ALine, AColumn, ASource);
+  FValues:=TJSElementNodes.Create(TJSElementNode);
+end;
+
+destructor TJSStructuredTypeDef.destroy;
+begin
+  FreeAndNil(FValues);
+  Inherited;
+end;
+
+procedure TJSStructuredTypeDef.AddValue(aElement: TJSElement);
+begin
+  FValues.AddNode.Node:=aElement;
+end;
+
+{ TJSMembersDeclaration }
+
+procedure TJSMembersDeclaration.SetMembers(AValue: TJSSourceElements);
+begin
+  if FMembers=AValue then Exit;
+  FMembers:=AValue;
+end;
+
+destructor TJSMembersDeclaration.Destroy;
+begin
+  inherited Destroy;
+end;
+
+{ TJSTypedParams }
+
+function TJSTypedParams.GetNames(aIndex : Integer): String;
+begin
+  Result:=Params[aIndex].Name;
+end;
+
+function TJSTypedParams.GetParams(aIndex : Integer): TJSTypedParam;
+begin
+  Result:=TJSTypedParam(Items[aIndex]);
+end;
+
+function TJSTypedParams.GetTypes(aIndex : Integer): TJSElement;
+begin
+  Result:=Params[aIndex].Node;
+end;
+
+function TJSTypedParams.AddParam(aName: jsBase.TJSString): TJSTypedParam;
+begin
+  Result:=add as TJSTypedParam;
+  Result.Name:=aName;
+end;
+
 { TJSAliasElements }
 
 function TJSAliasElements.GetA(AIndex: Integer): TJSAliasElement;
@@ -2034,13 +2642,31 @@ end;
 
 { TJSVarDeclaration }
 
+procedure TJSVarDeclaration.SetTyped(AValue: TJSTypeDef);
+begin
+  if FTyped=AValue then Exit;
+  if FOwnsType then
+    FreeAndNil(FTyped);
+  FTyped:=AValue;
+  FOwnsType:=aValue<>Nil;
+end;
 
 destructor TJSVarDeclaration.Destroy;
 begin
+  if FOwnsType then
+    FreeAndNil(FTyped);
   FreeAndNil(FInit);
   inherited Destroy;
 end;
 
+procedure TJSVarDeclaration.SetForeignType(AValue: TJSTypeDef);
+begin
+  if FOwnsType then
+    FreeAndNil(FTyped);
+  FTyped:=aValue;
+  FOwnsType:=False;
+end;
+
 { TJSIfStatement }
 
 destructor TJSIfStatement.Destroy;
@@ -2145,25 +2771,40 @@ end;
 
 constructor TJSSourceElements.Create(ALine, AColumn: Integer; const ASource: String
   );
+
+  Function CN(aName : String; DoClear : Boolean = False) : TJSElementNodes;
+  begin
+    Result:=TJSElementNodes.Create(TJSElementNode);
+    Result.NodeType:=aName;
+    Result.DoClearNodes:=DoClear;
+  end;
 begin
   inherited Create(ALine, AColumn, ASource);
-  FStatements:=TJSElementNodes.Create(TJSElementNode);
-  FFunctions:=TJSElementNodes.Create(TJSElementNode);
-  FVars:=TJSElementNodes.Create(TJSElementNode);
+  FStatements:=CN('Statements');
+  FFunctions:=CN('Functions');
+  FVars:=CN('Vars',True);
+  FClasses:=CN('Classes',True);
+  FModules:=CN('Modules');
+  FNamespaces:=CN('Namespaces');
+  FTypes:=CN('Types',True);
+  FInterfaces:=CN('Interfaces',True);
+  FEnums:=CN('Enums',True);
 end;
 
 destructor TJSSourceElements.Destroy;
 
-Var
-  i : integer;
 
 begin
+  // Vars, types, enums, classes, interfaces are owned by their statements, and those are freed later
+  FreeAndNil(FEnums);
+  FreeAndNil(FTypes);
+  FreeAndNil(FInterfaces);
+  FreeAndNil(FModules);
+  FreeAndNil(FNamespaces);
   FreeAndNil(FStatements);
   FreeAndNil(FFunctions);
-  // Vars are owned by their statements, and those have been freed
-  For I:=0 to FVars.Count-1 do
-    FVars.Nodes[i].Node:=nil;
   FreeAndNil(FVars);
+  FreeAndNil(FClasses);
   inherited Destroy;
 end;
 
@@ -2174,9 +2815,42 @@ begin
   Result:=TJSElementNode(Items[Aindex])
 end;
 
-function TJSElementNodes.AddNode: TJSElementNode;
+destructor TJSElementNodes.Destroy;
+begin
+  if FClearNodes then
+    ClearNodes;
+  inherited Destroy;
+end;
+
+procedure TJSElementNodes.ClearNodes;
+
+Var
+  I : Integer;
+
+begin
+  For I:=0 to Count-1 do
+     begin
+     if Assigned(Nodes[i].Node) then
+       begin
+       Write(FNodeType,': Clearing node ',I,': ');
+       WriteLn(Nodes[i].Node.ClassName)
+       end
+     else
+       Writeln(FNodeType,': Node ',i,'is nil');
+     Nodes[i].Node:=Nil;
+     end;
+end;
+
+function TJSElementNodes.AddNode(aIsAmbient : Boolean = False; aIsExport : Boolean = False): TJSElementNode;
 begin
   Result:=TJSElementNode(Add);
+  Result.IsAmbient:=aIsAmbient;
+  Result.IsExport:=aIsExport;
+end;
+
+function TJSElementNodes.AddNode(aEl: TJSElement; aIsAmbient: Boolean; aIsExport: Boolean): TJSElementNode;
+begin
+  AddNode(aIsAmbient,aIsExport).Node:=aEl;
 end;
 
 function TJSElementNodes.InsertNode(Index: integer): TJSElementNode;
@@ -2206,20 +2880,36 @@ procedure TJSFuncDef.SetParams(const AValue: TStrings);
 begin
   if FParams=AValue then exit;
   FParams.Assign(AValue);
+  TStringList(FParams).OwnsObjects:=True;
 end;
 
+
 constructor TJSFuncDef.Create;
 begin
   FParams:=TStringList.Create;
+  FTypedParams:=TJSTypedParams.Create(TJSTypedParam);
 end;
 
 destructor TJSFuncDef.Destroy;
 begin
+  FreeAndNil(FTypedParams);
   FreeAndNil(FBody);
   FreeAndNil(FParams);
+  FreeAndNil(FResultType);
   inherited Destroy;
 end;
 
+procedure TJSFuncDef.UpdateParams;
+
+Var
+  I : integer;
+
+begin
+  FParams.Clear;
+  For I:=0 to TypedParams.Count-1 do
+    FParams.Add(TypedParams.Names[i]);
+end;
+
 { TJSBracketMemberExpression }
 
 destructor TJSBracketMemberExpression.Destroy;

+ 1924 - 0
packages/fcl-js/src/tstopas.pp

@@ -0,0 +1,1924 @@
+{
+    This file is part of the Free Component Library
+
+    Typedescript declarations to pascal code converter
+    Copyright (c) 2021 by Michael Van Canneyt [email protected]
+
+    See the file COPYING.FPC, included in this distribution,
+    for details about the copyright.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+ **********************************************************************}
+unit tstopas;
+
+{$mode objfpc}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, contnrs, jsbase, jstree, jsscanner, jsparser,pascodegen;
+
+Type
+  ETSToPas = Class(Exception);
+
+  { TPasData }
+
+  TPasData = Class(TObject)
+  private
+    FOriginalName: String;
+    FPasName: String;
+  Public
+    Constructor Create(const aOriginalName : jsBase.TJSString; const APasName : String);
+    Destructor destroy; override;
+    Property PasName : String read FPasName;
+    Property OriginalName : String Read FOriginalName;
+  end;
+
+  TConversionOption = (coRaw,coGenericArrays,coUseNativeTypeAliases,coExternalConst,coExpandUnionTypeArgs,coaddOptionsToheader);
+  TConversionOptions = Set of TConversionOption;
+
+  TTypescriptToPas = Class;
+
+  { TTSContext }
+
+  TTSContext = class(TObject)
+  Private
+    FTypeMap : TFPObjectHashTable;
+    FTypeDeclarations : TFPObjectList;
+    FConverter : TTypescriptToPas;
+    procedure TypesToMap;
+  Public
+    Constructor Create(aConverter : TTypescriptToPas);
+    Destructor Destroy; override;
+    Procedure AddAliases(aAliases : TStrings);
+    Function FindTypeAlias(aName : jsbase.TJSString) : String;
+    Procedure AddToTypeMap(aName : UTF8String; const aPasName : String);
+    Procedure AddToTypeMap(aName : jsbase.TJSString; const aPasName : String);
+    Procedure AddToTypeMap(aType : TJSElement);
+    Property TypeMap : TFPObjectHashTable Read FTypeMap;
+  end;
+
+  { TTSJSScanner }
+
+  TTSJSScanner = class(TJSScanner)
+  private
+    FContext: TTSContext;
+  Public
+    Property Context : TTSContext Read FContext Write FContext;
+  end;
+
+  { TTSJSParser }
+
+  TTSJSParser = class(TJSParser)
+  private
+    FContext: TTSContext;
+  Protected
+    Function CreateElement(AElementClass : TJSElementClass)  : TJSElement; override;
+  Public
+    Property Context : TTSContext Read FContext Write FContext;
+  end;
+
+  { TTypescriptToPas }
+
+  TTypescriptToPas = Class(TPascalCodeGenerator)
+  private
+    FClassPrefix: String;
+    FClassSuffix: String;
+    FContext: TTSContext;
+    FDictionaryClassParent: String;
+    FElements: TJSFunctionBody;
+    FFieldPrefix: String;
+    FIncludeImplementationCode: TStrings;
+    FIncludeInterfaceCode: TStrings;
+    FInputFileName: String;
+    FInputStream: TStream;
+    FOptions: TConversionOptions;
+    FOutputFileName: String;
+    FTypeAliases: TStrings;
+    FVerbose: Boolean;
+    FECMAVersion: TECMAVersion;
+    FPasNameList : TFPObjectList;
+    FAutoTypes : TStrings;
+    procedure DumpElements;
+    function GetIsRaw: Boolean;
+    procedure SetIncludeImplementationCode(AValue: TStrings);
+    procedure SetIncludeInterfaceCode(AValue: TStrings);
+    procedure SetTypeAliases(AValue: TStrings);
+  Protected
+    function GetGenericParams(aTypeParams: TJSElementNodes): String; virtual;
+    procedure AddOptionsToHeader;
+    Procedure Parse; virtual;
+    Procedure WritePascal; virtual;
+    function CreateParser(aContext: TTSContext; S: TJSScanner): TJSParser; virtual;
+    function CreateScanner(aContext : TTSContext; S: TStream): TJSScanner;virtual;
+    Function CreateContext : TTSContext; virtual;
+    Function BaseUnits : String; override;
+    // Auxiliary routines
+    procedure Getoptions(L: TStrings); virtual;
+    procedure ProcessDefinitions; virtual;
+    function CreatePasName(const aOriginal : jsBase.TJSString; const aName: String): TPasData;virtual;
+    procedure AllocatePasNames(aList: TJSSourceElements; ParentName: String=''); virtual;
+    procedure AllocatePasNames(aList: TJSElementNodes; ParentName: String=''); virtual;
+    Function AllocatePasName(D: TJSElement; ParentName: String='') : TPasData;virtual;
+    procedure EnsureUniqueNames(ML: TJSSourceElements);virtual;
+    function GetName(ADef: TJSElement): String;virtual;
+    function HaveConsts(aList: TJSSourceElements): Boolean;virtual;
+    function GetTypeName(Const aTypeName: JSBase.TJSString; ForTypeDef: Boolean=False): String;virtual;
+    function GetTypeName(aTypeDef: TJSTypeDef; ForTypeDef: Boolean=False): String;virtual;
+{    function AddSequenceDef(ST: TIDLSequenceTypeDefDefinition): Boolean; virtual;
+    function CheckUnionTypeDefinition(D: TIDLDefinition): TIDLUnionTypeDefDefinition;virtual;
+    procedure AddArgumentToOverloads(aList: TFPObjectlist; AName, ATypeName: String);virtual;
+    procedure AddUnionOverloads(aList: TFPObjectlist; AName: String;  UT: TIDLUnionTypeDefDefinition);virtual;
+    procedure AddArgumentToOverloads(aList: TFPObjectlist; adef: TIDLArgumentDefinition);virtual;
+    procedure AddOverloads(aList: TFPObjectlist; adef: TIDLFunctionDefinition; aIdx: Integer);virtual;
+    function CloneNonPartialArgumentList(aList: TFPObjectlist; ADest: TFPObjectlist= Nil; AsPartial: Boolean=True): integer;virtual;
+    function GetOverloads(aDef: TIDLFunctionDefinition): TFPObjectlist;virtual;
+    function GetArguments(aList: TIDLDefinitionList; ForceBrackets: Boolean): String;virtual;
+    // Actual code generation routines
+    // Lists. Return the number of actually written defs.
+    function WriteCallBackDefs(aList: TIDLDefinitionList): Integer; virtual;
+    Function WriteDictionaryDefs(aList: TIDLDefinitionList) : Integer;virtual;
+    Function WriteForwardClassDefs(aList: TIDLDefinitionList) : Integer;virtual;
+    Function WriteInterfaceDefs(aList: TIDLDefinitionList) : Integer;virtual;
+    Function WriteMethodDefs(aList: TIDLDefinitionList) : Integer;virtual;
+    Function WriteEnumDefs(aList: TIDLDefinitionList) : Integer;virtual;
+    function WriteConsts(aList: TIDLDefinitionList): Integer;virtual;
+    function WritePlainFields(aList: TIDLDefinitionList): Integer;virtual;
+    function WriteDictionaryFields(aList: TIDLDefinitionList): Integer;virtual;
+    function WritePrivateReadOnlyFields(aList: TIDLDefinitionList): Integer;virtual;
+    // Actual definitions. Return true if a definition was written.
+    Function WriteForwardClassDef(D: TIDLStructuredDefinition) : Boolean;virtual;
+    function WriteFunctionTypeDefinition(aDef: TIDLFunctionDefinition): Boolean;virtual;
+    function WriteFunctionDefinition(aDef: TIDLFunctionDefinition): Boolean;virtual;
+    function WriteTypeDef(aDef: TIDLTypeDefDefinition): Boolean; virtual;
+    function WriteRecordDef(aDef: TIDLRecordDefinition): Boolean; virtual;
+    function WriteEnumDef(aDef: TIDLEnumDefinition): Boolean; virtual;
+    function WriteDictionaryField(aField: TIDLDictionaryMemberDefinition): Boolean;virtual;
+    Function WritePrivateReadOnlyField(aAttr: TIDLAttributeDefinition) : Boolean;virtual;
+    Function WriteField(aAttr: TIDLAttributeDefinition) : Boolean;virtual;
+    Function WriteReadonlyProperty(aAttr: TIDLAttributeDefinition) : Boolean;virtual;
+    Function WriteConst(aConst: TIDLConstDefinition) : Boolean ;virtual;
+    function WriteInterfaceDef(Intf: TIDLInterfaceDefinition): Boolean; virtual;
+    function WriteDictionaryDef(aDict: TIDLDictionaryDefinition): Boolean; virtual;
+    // Additional
+    procedure WritePromiseDef(aDef: TIDLPromiseTypeDefDefinition);virtual;
+    procedure WriteSequenceDef(aDef: TIDLSequenceTypeDefDefinition);virtual;
+    procedure WriteUnionDef(aDef: TIDLUnionTypeDefDefinition);virtual;
+    }
+    // Properties
+    procedure WritePropertyDeclaration(D: TJSVariableStatement);
+    function WriteProperties(aClass: TJSClassDeclaration): Integer;
+    // Variables
+    procedure WriteVariable(aVar: TJSVarDeclaration);
+    procedure WriteVariables(Vars: TJSElementNodes); virtual;
+    // Get type defs as string
+    function GetTypeAsString(aType: TJSTypeDef; asPascal, asSubType: Boolean): String;
+    function GetArrayTypeAsString(aTypeDef: TJSArrayTypeDef; asPascal, asSubType: Boolean): String;
+    function GetAliasTypeAsString(aTypeDef: TJSTypeReference; asPascal, asSubType: Boolean): string;
+    function GetIntersectionTypeAsString(aTypeDef: TJSIntersectionTypeDef; asPascal, asSubType: Boolean): String;
+    function GetUnionTypeAsString(aTypeDef: TJSUnionTypeDef; asPascal, asSubType: Boolean): String;
+
+    // Write types
+    procedure WriteTypeDefs(Types: TJSElementNodes); virtual;
+    procedure WriteAliasTypeDef(const aPasName: string; const aOrgName: jsBase.TJSString; aTypeParams: TJSElementNodes;  aTypeDef: TJSTypeReference); virtual;
+    procedure WriteTypeDef(const aPasName: string; const aOrgName: jsBase.TJSString; aTypeParams: TJSElementNodes; aTypeDef: TJSTypeDef);
+    procedure WriteUnionTypeDef(const aPasName: string; const aOrgName: jsBase.TJSString; aTypeParams: TJSElementNodes; aTypeDef: TJSUnionTypeDef);
+    procedure WriteIntersectionTypeDef(const aPasName: string; const aOrgName: jsBase.TJSString; aTypeParams: TJSElementNodes; aTypeDef: TJSIntersectionTypeDef);
+    procedure WriteArrayTypeDef(const aPasName: string; const aOrgName: jsBase.TJSString; aTypeParams: TJSElementNodes;  aTypeDef: TJSArrayTypeDef);
+
+    // Extra interface/Implementation code.
+    procedure WriteImplementation; virtual;
+    procedure WriteIncludeInterfaceCode; virtual;
+    Property Elements : TJSFunctionBody Read FElements;
+    Property Context : TTSContext Read FContext;
+    Property IsRaw : Boolean Read GetIsRaw;
+  Public
+    Constructor Create(Aowner : TComponent); override;
+    Destructor Destroy; override;
+    Procedure Execute;
+    Property InputStream : TStream Read FInputStream Write FInputStream;
+  Published
+    Property InputFileName : String Read FInputFileName Write FInputFileName;
+    Property OutputFileName : String Read FOutputFileName Write FOutputFileName;
+    Property Verbose : Boolean Read FVerbose Write FVerbose;
+    Property FieldPrefix : String Read FFieldPrefix Write FFieldPrefix;
+    Property ClassPrefix : String Read FClassPrefix Write FClassPrefix;
+    Property ClassSuffix : String Read FClassSuffix Write FClassSuffix;
+    Property Options : TConversionOptions Read FOptions Write FOptions;
+    Property ECMAVersion : TECMAVersion Read FECMAVersion Write FECMAVersion;
+    Property TypeAliases : TStrings Read FTypeAliases Write SetTypeAliases;
+    Property IncludeInterfaceCode : TStrings Read FIncludeInterfaceCode Write SetIncludeInterfaceCode;
+    Property IncludeImplementationCode : TStrings Read FIncludeImplementationCode Write SetIncludeImplementationCode;
+    Property DictionaryClassParent : String Read FDictionaryClassParent Write FDictionaryClassParent;
+  end;
+
+implementation
+
+uses typinfo;
+
+{ TTSJSParser }
+
+function TTSJSParser.CreateElement(AElementClass: TJSElementClass): TJSElement;
+begin
+  Result:=inherited CreateElement(AElementClass);
+  If Result is TJSTypeDeclaration then
+    FContext.AddToTypeMap(Result);
+end;
+
+{ TTSContext }
+
+constructor TTSContext.Create(aConverter : TTypescriptToPas);
+begin
+  FConverter:=aConverter;
+  FTypeMap:=TFPObjectHashTable.Create(False);
+  FTypeDeclarations:=TFPObjectList.Create(False);
+end;
+
+destructor TTSContext.Destroy;
+begin
+  FreeAndNil(FTypeDeclarations);
+  FreeAndNil(FTypeMap);
+  inherited Destroy;
+end;
+
+procedure TTSContext.AddAliases(aAliases: TStrings);
+
+Var
+  I : Integer;
+  N,V : String;
+
+begin
+  For I:=0 to aAliases.Count-1 do
+    begin
+    aAliases.GetNameValue(I,N,V);
+    AddToTypeMap(UTF8String(N),V);
+    end;
+end;
+
+function TTSContext.FindTypeAlias(aName: jsbase.TJSString): String;
+
+Var
+  S : UTF8String;
+  Obj : TObject;
+
+begin
+  S:=UTF8Encode(aName);
+  if FTypeDeclarations.Count>0 then
+    TypesToMap;
+  Obj:=FTypeMap.Items[S];
+  if (Obj is TPasData) then
+    Result:=TPasData(Obj).PasName
+  else
+    Result:=S;
+end;
+
+procedure TTSContext.TypesToMap;
+
+Var
+  I : Integer;
+  el : TJSElement;
+
+begin
+  For I:=0 to FTypeDeclarations.Count-1 do
+    begin
+    El:=TJSElement(FTypeDeclarations[i]);
+    if El.Data=Nil then
+      FConverter.AllocatePasName(El,'');
+    FTypeMap.Add(TPasData(El.Data).OriginalName,El.Data) ;
+    end;
+  FTypeDeclarations.Clear;
+end;
+
+procedure TTSContext.AddToTypeMap(aName: UTF8String; const aPasName: String);
+begin
+  FTypeMap.Add(aName,FConverter.CreatePasName(aName,aPasName));
+end;
+
+procedure TTSContext.AddToTypeMap(aName: jsbase.TJSString; const aPasName: String);
+begin
+  AddToTypeMap(UTF8Encode(aName),aPasName);
+end;
+
+procedure TTSContext.AddToTypeMap(aType: TJSElement);
+begin
+  FTypeDeclarations.Add(aType);
+end;
+
+{ TPasData }
+
+constructor TPasData.Create(const aOriginalName : jsBase.TJSString; const APasName : String);
+begin
+  FOriginalName:=aOriginalName;
+  FPasName:=APasName;
+end;
+
+destructor TPasData.destroy;
+begin
+  Writeln('Destroying ',Self.FOriginalName,'->',Self.Pasname);
+  inherited destroy;
+end;
+
+{ TTypescriptToPas }
+
+function TTypescriptToPas.CreateContext: TTSContext;
+begin
+  Result:=TTSContext.Create(Self);
+end;
+
+function TTypescriptToPas.CreateScanner(aContext : TTSContext; S : TStream) :  TJSScanner;
+
+begin
+  Result:=TTSJSScanner.Create(S,FECMAVersion);
+  Result.IsTypeScript:=True;
+end;
+
+function TTypescriptToPas.CreateParser(aContext : TTSContext;S : TJSScanner) :  TJSParser;
+
+begin
+  Result:=TTSJSParser.Create(S);
+  TTSJSParser(Result).Context:=aContext;
+end;
+
+procedure TTypescriptToPas.DumpElements;
+
+  Procedure DumpNodes(Const aSection : String; aList: TJSElementNodes);
+  Var
+    I : Integer;
+    N : TJSElementNode;
+
+  begin
+    Writeln(aSection,': ',aList.Count,' elements');
+    For I:=0 to aList.Count-1 do
+      begin
+      N:=Alist[i];
+      Writeln(aSection,' element ',I,' : ',N.Node.ClassName);
+      end;
+  end;
+
+Var
+  Els : TJSSourceElements;
+
+begin
+  Els:=FElements.A as TJSSourceElements;
+  DumpNodes('vars',Els.Vars);
+  DumpNodes('statements',Els.Statements);
+  DumpNodes('classes',Els.Classes);
+  DumpNodes('types',Els.Types);
+  DumpNodes('enums',Els.Enums);
+  DumpNodes('functions',Els.Functions);
+  DumpNodes('namespaces',Els.Namespaces);
+end;
+
+procedure TTypescriptToPas.Parse;
+
+Var
+  F : TStream;
+  S : TJSScanner;
+  P : TJSParser;
+  El : TJSElement;
+
+begin
+  FreeAndNil(FElements); // In case parse is called multiple times
+  P:=Nil;
+  F:=InputStream;
+  if (F=Nil) then
+    F:=TFileStream.Create(InputFileName,fmOpenRead or fmShareDenyWrite);
+  try
+    S:=CreateScanner(Context,F);
+    P:=CreateParser(Context,S);
+    El:=P.Parse;
+    if not (El is TJSFunctionBody) then
+      begin
+      EL.Free;
+      Raise ETStoPas.Create('Parse result is not a function body');
+      end;
+    FElements:=El as TJSFunctionBody;
+    // DumpElements;
+  finally
+    P.Free;
+    S.Free;
+    if F<>InputStream then
+      F.Free;
+  end;
+end;
+
+function TTypescriptToPas.GetName(ADef: TJSElement): String;
+
+begin
+  If Assigned(ADef) and (TObject(ADef.Data) is TPasData) then
+    Result:=TPasData(ADef.Data).PasName
+  else if aDef is TJSNamedElement then
+    Result:=TJSNamedElement(ADef).Name
+  else
+    Result:='';
+end;
+
+function TTypescriptToPas.HaveConsts(aList: TJSSourceElements): Boolean;
+
+Var
+  I : Integer;
+  D : TJSVariableStatement;
+
+begin
+  Result:=False;
+  For I:=0 to aList.Vars.Count-1 do
+    begin
+    D:=aList.Vars[i].Node as TJSVariableStatement;
+    if (D.VarType=vtConst) then
+      Exit(True);
+    end;
+end;
+
+function TTypescriptToPas.GetTypeName(const aTypeName: jsBase.TJSString; ForTypeDef: Boolean): String;
+
+
+  Function UsePascalType(Const aPascalType : string) : String;
+
+  begin
+    if (coUseNativeTypeAliases in Options) and ForTypeDef then
+      Result:=StringReplace(UTF8Encode(aTypeName),' ','',[rfReplaceAll])
+    else
+      Result:=aPascalType;
+  end;
+
+Var
+  TN : UTF8String;
+
+begin
+  Case aTypeName of
+    'union': TN:='JSValue';
+    'short': TN:=UsePascalType('Integer');
+    'long': TN:=UsePascalType('Integer');
+    'long long': TN:=UsePascalType('NativeInt');
+    'unsigned short': TN:=UsePascalType('Cardinal');
+    'unrestricted float': TN:=UsePascalType('Double');
+    'unrestricted double': TN:=UsePascalType('Double');
+    'unsigned long': TN:=UsePascalType('NativeInt');
+    'unsigned long long': TN:=UsePascalType('NativeInt');
+    'octet': TN:=UsePascalType('Byte');
+    'any' : TN:=UsePascalType('JSValue');
+    'number' : TN:=UsePascalType('Double');
+    'float' : TN:=UsePascalType('Double');
+    'double' : TN:=UsePascalType('Double');
+    'DOMString',
+    'USVString',
+    'ByteString' : TN:=UsePascalType('String');
+    'object' : TN:=UsePascalType('TJSObject');
+    'Error' : TN:=UsePascalType('TJSError');
+    'DOMException' : TN:=UsePascalType('TJSError');
+    'ArrayBuffer',
+    'DataView',
+    'Int8Array',
+    'Int16Array',
+    'Int32Array',
+    'Uint8Array',
+    'Uint16Array',
+    'Uint32Array',
+    'Uint8ClampedArray',
+    'Float32Array',
+    'Float64Array' : TN:='TJS'+UTF8Encode(aTypeName);
+  else
+    TN:=FContext.FindTypeAlias(aTypeName);
+  end;
+  Result:=TN;
+end;
+
+
+function TTypescriptToPas.GetTypeName(aTypeDef : TJSTypeDef; ForTypeDef : Boolean = False): String;
+
+begin
+  if (aTypeDef.Data is TPasData) then
+    Result:=TPasData(aTypeDef.Data).PasName
+  else if ATypeDef is TJSTypeReference then
+    Result:=GetTypeName(TJSTypeReference(aTypeDef).Name,ForTypeDef)
+  else
+    Raise ETSToPas.Create('Cannot get type name from '+aTypeDef.ClassName);
+end;
+
+
+function TTypescriptToPas.WriteProperties(aClass: TJSClassDeclaration): Integer;
+
+Var
+  I : Integer;
+  D : TJSVariableStatement;
+
+begin
+  For I:=0 to aClass.Members.Vars.Count-1 do
+    begin
+    D:=aClass.Members.Vars[i].Node as TJSVariableStatement;
+    if (D.VarType=vtVar) then
+      WritePropertyDeclaration(D);
+    end;
+end;
+
+function TTypescriptToPas.GetGenericParams(aTypeParams: TJSElementNodes) : String;
+
+Var
+  I : Integer;
+  aName: jsBase.TJSString;
+
+begin
+  Result:='';
+  if aTypeParams=nil then exit;
+  For I:=0 to aTypeParams.Count-1 do
+    begin
+    aName:=(aTypeParams[i].Node as TJSTypeReference).Name;
+    if Result<>'' then
+      Result:=Result+',';
+    Result:=Result+aName;
+    end;
+  if Result<>'' then
+    Result:='<'+Result+'>';
+end;
+
+Function TTypescriptToPas.GetAliasTypeAsString(aTypeDef : TJSTypeReference; asPascal, asSubType: Boolean) : string;
+
+begin
+  if asPascal then
+    Result:=GetTypeName(aTypeDef.Name,True)
+  else
+    Result:=aTypeDef.Name
+end;
+
+procedure TTypescriptToPas.WriteAliasTypeDef(const aPasName : string; const aOrgName : jsBase.TJSString; aTypeParams: TJSElementNodes; aTypeDef : TJSTypeReference);
+
+Var
+  TN, gen, genparams: String;
+
+begin
+  TN:=GetAliasTypeAsString(aTypeDef,True,False);
+  genparams:=GetGenericParams(aTypeParams);
+  if (genparams<>'') then
+    gen:='generic ';
+  AddLn('%s%s%s = %s;',[gen,aPasName,genparams,TN]);
+end;
+
+procedure TTypescriptToPas.WriteImplementation;
+begin
+
+end;
+
+Procedure TTypescriptToPas.WritePropertyDeclaration(D : TJSVariableStatement);
+
+begin
+end;
+
+(*
+function TTypescriptToPas.WriteConst(aConst: T): Boolean;
+
+Const
+  ConstTypes : Array[TConstType] of String =
+     ('Double','NativeInt','Boolean','JSValue','JSValue','JSValue','JSValue','String','JSValue','JSValue');
+Var
+  S : String;
+
+begin
+  Result:=True;
+  // Consts cannot be strings
+  if coExternalConst in Options then
+    begin
+    S:=ConstTypes[aConst.ConstType];
+    Addln('%s : %s;',[GetName(aConst),S])
+    end
+  else
+    begin
+    S:=aConst.Value;
+    if aConst.ConstType=ctInteger then
+      S:=StringReplace(S,'0x','$',[]);
+    Addln('%s = %s;',[GetName(aConst),S])
+    end;
+end;
+
+function TTypescriptToPas.WriteConsts(aList: TIDLDefinitionList): Integer;
+
+Var
+  D : TIDLDefinition;
+
+begin
+  EnsureSection(csConst);
+  Indent;
+  Result:=0;
+  For D in aList do
+    if D is TIDLConstDefinition then
+      if WriteConst(D as TIDLConstDefinition) then
+        Inc(Result);
+  Undent;
+end;
+
+function TTypescriptToPas.WritePlainFields(aList: TIDLDefinitionList): Integer;
+
+Var
+  D : TIDLDefinition;
+  A : TIDLAttributeDefinition absolute D;
+
+begin
+  EnsureSection(csDeclaration);
+  Indent;
+  Result:=0;
+  For D in aList do
+    if D is TIDLAttributeDefinition then
+      if Not (aoReadOnly in A.Options) then
+        if WriteField(A) then
+          Inc(Result);
+  Undent;
+end;
+
+function TTypescriptToPas.WriteDictionaryField(
+  aField: TIDLDictionaryMemberDefinition): Boolean;
+
+Var
+  Def,N,TN : String;
+
+begin
+  Result:=True;
+  N:=GetName(aField);
+  TN:=GetTypeName(aField.MemberType);
+  if TN='record' then
+    TN:='TJSObject';
+  if SameText(N,TN) then
+    N:='_'+N;
+  Def:=Format('%s : %s;',[N,TN]);
+  if (N<>aField.Name) then
+    Def:=Def+Format('external name ''%s'';',[aField.Name]);
+  AddLn(Def);
+end;
+
+function TTypescriptToPas.WriteDictionaryFields(aList: TIDLDefinitionList): Integer;
+
+Var
+  D : TIDLDefinition;
+  M : TIDLDictionaryMemberDefinition absolute D;
+
+begin
+  Indent;
+  Result:=0;
+  For D in aList do
+    if D is TIDLDictionaryMemberDefinition then
+      if WriteDictionaryField(M) then
+        Inc(Result);
+  Undent;
+end;
+
+function TTypescriptToPas.WriteMethodDefs(aList: TIDLDefinitionList): Integer;
+
+Var
+  D : TIDLDefinition;
+  FD : TIDLFunctionDefinition absolute D;
+
+begin
+  Result:=0;
+  for D in aList do
+    if D is TIDLFunctionDefinition then
+      if Not (foCallBack in FD.Options) then
+         if WriteFunctionDefinition(FD) then
+           Inc(Result);
+end;
+
+function TTypescriptToPas.AddSequenceDef(ST: TIDLSequenceTypeDefDefinition
+  ): Boolean;
+
+var
+  TN : String;
+begin
+  TN:=GetTypeName(ST);
+  Result:=FAutoTypes.IndexOf(TN)=-1;
+  if Result then
+    begin
+    FAutoTypes.Add(TN);
+    DoLog('Automatically adding %s sequence definition.',[TN]);
+    AddLn('%s = Array of %s;',[TN,GetTypeName(ST.ElementType)]);
+    ST.Data:=CreatePasName(TN);
+    end;
+end;
+
+function TTypescriptToPas.WriteFunctionImplicitTypes(aList: TIDLDefinitionList): Integer;
+
+Var
+  D,D2,D3 : TIDLDefinition;
+  FD : TIDLFunctionDefinition absolute D;
+  DA : TIDLArgumentDefinition absolute D2;
+  UT : TIDLUnionTypeDefDefinition;
+
+begin
+  Result:=0;
+  for D in aList do
+    if D is TIDLFunctionDefinition then
+      if Not (foCallBack in FD.Options) then
+        begin
+        if (FD.ReturnType is TIDLSequenceTypeDefDefinition) then
+          if AddSequenceDef(FD.ReturnType as TIDLSequenceTypeDefDefinition) then
+            Inc(Result);
+        For D2 in FD.Arguments do
+          if (DA.ArgumentType is TIDLSequenceTypeDefDefinition) then
+            begin
+            if AddSequenceDef(DA.ArgumentType as TIDLSequenceTypeDefDefinition) then
+              Inc(Result);
+            end
+          else
+            begin
+            UT:=CheckUnionTypeDefinition(DA.ArgumentType);
+            if Assigned(UT) then
+              For D3 in UT.Union do
+                if (D3 is TIDLSequenceTypeDefDefinition) then
+                  if AddSequenceDef(D3 as TIDLSequenceTypeDefDefinition) then
+                    Inc(Result);
+            end;
+        end;
+  if Result>0 then
+    AddLn('');
+end;
+
+function TTypescriptToPas.WriteAttributeImplicitTypes(aList: TIDLDefinitionList
+  ): Integer;
+Var
+  D : TIDLDefinition;
+  FA : TIDLAttributeDefinition absolute D;
+
+begin
+  Result:=0;
+  for D in aList do
+    if D is TIDLAttributeDefinition then
+      if (FA.AttributeType is TIDLSequenceTypeDefDefinition) then
+        if AddSequenceDef(FA.AttributeType as TIDLSequenceTypeDefDefinition) then
+          Inc(Result);
+end;
+
+function TTypescriptToPas.WriteDictionaryMemberImplicitTypes(
+  aList: TIDLDefinitionList): Integer;
+
+Var
+  D : TIDLDefinition;
+  FD : TIDLDictionaryMemberDefinition absolute D;
+
+begin
+  Result:=0;
+  for D in aList do
+    if D is TIDLDictionaryMemberDefinition then
+      if (FD.MemberType is TIDLSequenceTypeDefDefinition) then
+        if AddSequenceDef(FD.MemberType as TIDLSequenceTypeDefDefinition) then
+          Inc(Result);
+end;
+
+procedure TTypescriptToPas.EnsureUniqueNames(ML : TIDLDefinitionList);
+
+Var
+  L : TFPObjectHashTable;
+
+  Procedure CheckRename(aD : TIDLDefinition);
+
+  var
+    I : integer;
+    NOrig,N,N2 : String;
+    isDup : Boolean;
+    D2 : TIDLDefinition;
+
+  begin
+    NOrig:=GetName(aD);
+    N:=LowerCase(NOrig);
+    N2:=N;
+    I:=0;
+    isDup:=False;
+    Repeat
+      D2:=TIDLDefinition(L.Items[N2]);
+      if (D2<>Nil) then
+        // Overloads
+        begin
+        isDup:=((D2 is TIDLFunctionDefinition) and (ad is TIDLFunctionDefinition));
+        if IsDup then
+          D2:=Nil
+        else
+          begin
+          inc(I);
+          N2:=KeywordPrefix+N+KeywordSuffix;
+          Norig:=KeywordPrefix+NOrig+KeywordSuffix;
+          end;
+        end;
+    Until (D2=Nil);
+    if (N<>N2) then
+      begin
+      N:=GetName(aD);
+      DoLog('Renaming duplicate identifier (%s) %s to %s',[aD.ClassName,N,Norig]);
+      // Original TPasName is in list, will be freed automatically
+      aD.Data:=CreatePasName(NOrig);
+      end;
+    if not IsDup then
+      L.Add(N2,aD);
+  end;
+
+var
+  D : TIDLDefinition;
+
+begin
+  L:=TFPObjectHashTable.Create(False);
+  try
+    For D in ML Do
+      if not (D is TIDLConstDefinition) then
+        CheckRename(D);
+    For D in ML Do
+      if (D is TIDLConstDefinition) then
+        CheckRename(D);
+  finally
+    L.Free;
+  end;
+end;
+
+function TTypescriptToPas.WriteInterfaceDef(Intf: TIDLInterfaceDefinition): Boolean;
+
+Var
+  CN,PN : String;
+  Decl : String;
+  ML : TIDLDefinitionList;
+
+begin
+  Result:=True;
+  ML:=TIDLDefinitionList.Create(Nil,False);
+  try
+    Intf.GetFullMemberList(ML);
+    EnsureUniqueNames(ML);
+    CN:=GetName(Intf);
+    ClassHeader(CN);
+    WriteFunctionImplicitTypes(ML);
+    WriteAttributeImplicitTypes(ML);
+    Decl:=Format('%s = class external name %s ',[CN,MakePascalString(Intf.Name,True)]);
+    if Assigned(Intf.ParentInterface) then
+      PN:=GetName(Intf.ParentInterface)
+    else
+      PN:=GetTypeName(Intf.ParentName);
+    if PN<>'' then
+      Decl:=Decl+Format(' (%s)',[PN]);
+    AddLn(Decl);
+    AddLn('Private');
+    Indent;
+    WritePrivateReadOnlyFields(ML);
+    Undent;
+    AddLn('Public');
+    if HaveConsts(ML) then
+      begin
+      Indent;
+      PushSection(csUnknown);
+      WriteConsts(ML);
+      PopSection;
+      Undent;
+      AddLn('Public');
+      end;
+    Indent;
+    WritePlainFields(ML);
+    WriteMethodDefs(ML);
+    WriteProperties(ML);
+    Undent;
+    AddLn('end;');
+  finally
+    ML.Free;
+  end;
+end;
+
+function TTypescriptToPas.WriteDictionaryDef(aDict: TIDLDictionaryDefinition
+  ): Boolean;
+
+Var
+  CN,CP : String;
+  ML : TIDLDefinitionList;
+  PD: TIDLDictionaryDefinition;
+
+begin
+  Result:=True;
+  ML:=TIDLDefinitionList.Create(Nil,False);
+  try
+    PD:=aDict;
+    While PD<>Nil do
+      begin
+      PD.GetFullMemberList(ML);
+      PD:=PD.ParentDictionary;
+      end;
+    CN:=GetName(aDict);
+    CP:=DictionaryClassParent;
+    if CP='' then
+      CP:='TJSObject';
+    ClassHeader(CN);
+    WriteDictionaryMemberImplicitTypes(ML);
+    if (coDictionaryAsClass in Options) then
+      Addln('%s = class(%s)',[CN,CP])
+    else
+      Addln('%s = record',[CN]);
+    WriteDictionaryFields(ML);
+    AddLn('end;');
+  finally
+    ML.Free;
+  end;
+end;
+
+procedure TTypescriptToPas.WriteImplementation;
+
+Var
+  S : String;
+
+begin
+  Addln('');
+  For S in FIncludeImplementationCode do
+    Addln(S);
+  Addln('');
+end;
+
+function TTypescriptToPas.GetTypeName(aTypeDef : TIDLTypeDefDefinition; ForTypeDef : Boolean = False): String;
+
+begin
+  if ATypeDef is TIDLSequenceTypeDefDefinition then
+    begin
+    if Assigned(aTypeDef.Data) then
+      Result:=GetName(aTypeDef)
+    else
+      begin
+      Result:=GetTypeName(TIDLSequenceTypeDefDefinition(aTypeDef).ElementType,ForTypeDef);
+      Result:='T'+Result+'DynArray';
+      end
+    end
+  else
+    Result:=GetTypeName(aTypeDef.TypeName,ForTypeDef);
+end;
+
+function TTypescriptToPas.GetTypeName(const aTypeName: String; ForTypeDef: Boolean
+  ): String;
+
+
+  Function UsePascalType(Const aPascalType : string) : String;
+
+  begin
+    if (coUseNativeTypeAliases in Options) and ForTypeDef then
+      Result:=StringReplace(aTypeName,' ','',[rfReplaceAll])
+    else
+      Result:=aPascalType;
+  end;
+
+Var
+  A,TN : UTF8String;
+  D : TIDLDefinition;
+
+begin
+  Case aTypeName of
+    'union': TN:='JSValue';
+    'short': TN:=UsePascalType('Integer');
+    'long': TN:=UsePascalType('Integer');
+    'long long': TN:=UsePascalType('NativeInt');
+    'unsigned short': TN:=UsePascalType('Cardinal');
+    'unrestricted float': TN:=UsePascalType('Double');
+    'unrestricted double': TN:=UsePascalType('Double');
+    'unsigned long': TN:=UsePascalType('NativeInt');
+    'unsigned long long': TN:=UsePascalType('NativeInt');
+    'octet': TN:=UsePascalType('Byte');
+    'any' : TN:=UsePascalType('JSValue');
+    'float' : TN:=UsePascalType('Double');
+    'double' : TN:=UsePascalType('Double');
+    'DOMString',
+    'USVString',
+    'ByteString' : TN:=UsePascalType('String');
+    'object' : TN:=UsePascalType('TJSObject');
+    'Error' : TN:=UsePascalType('TJSError');
+    'DOMException' : TN:=UsePascalType('TJSError');
+    'ArrayBuffer',
+    'DataView',
+    'Int8Array',
+    'Int16Array',
+    'Int32Array',
+    'Uint8Array',
+    'Uint16Array',
+    'Uint32Array',
+    'Uint8ClampedArray',
+    'Float32Array',
+    'Float64Array' : TN:='TJS'+aTypeName;
+  else
+    TN:=aTypeName;
+    D:=FContext.FindDefinition(TN);
+    if D<>Nil then
+      TN:=GetName(D)
+    else
+      begin
+      A:=FTypeAliases.Values[TN];
+      If (A<>'') then
+        TN:=A;
+      end;
+  end;
+  Result:=TN;
+end;
+
+function TTypescriptToPas.WritePrivateReadOnlyField(aAttr: TIDLAttributeDefinition
+  ): Boolean;
+
+begin
+  AddLn('%s%s : %s; external name ''%s''; ',[FieldPrefix,GetName(aAttr),GetTypeName(aAttr.AttributeType),aAttr.Name]);
+end;
+
+function TTypescriptToPas.WriteField(aAttr: TIDLAttributeDefinition): Boolean;
+
+Var
+  Def,TN,N : String;
+
+begin
+  Result:=True;
+  N:=GetName(aAttr);
+  TN:=GetTypeName(aAttr.AttributeType);
+  if TN='record' then
+    TN:='TJSObject';
+  if SameText(N,TN) then
+    N:='_'+N;
+  Def:=Format('%s : %s;',[N,TN]);
+  if (N<>aAttr.Name) then
+    Def:=Def+Format('external name ''%s'';',[aAttr.Name]);
+  AddLn(Def);
+end;
+
+function TTypescriptToPas.WriteReadonlyProperty(aAttr: TIDLAttributeDefinition
+  ): Boolean;
+
+Var
+  TN,N,PN : String;
+
+begin
+  Result:=True;
+  N:=GetName(aAttr);
+  PN:=N;
+  TN:=GetTypeName(aAttr.AttributeType);
+  if SameText(PN,TN) then
+    PN:='_'+PN;
+  AddLn('Property %s : %s Read %s%s; ',[PN,TN,FieldPrefix,N]);
+end;
+
+
+function TTypescriptToPas.WriteForwardClassDef(D: TIDLStructuredDefinition): Boolean;
+
+begin
+  Result:=not D.IsPartial;
+  if Result then
+    AddLn('%s = Class;',[GetName(D)]);
+end;
+
+function TTypescriptToPas.WriteForwardClassDefs(aList: TIDLDefinitionList): Integer;
+
+Var
+  D : TIDLDefinition;
+
+begin
+  Result:=0;
+  Comment('Forward class definitions');
+  For D in aList do
+    if D is TIDLInterfaceDefinition then
+      if WriteForwardClassDef(D as TIDLInterfaceDefinition) then
+        Inc(Result);
+  if coDictionaryAsClass in Options then
+    For D in aList do
+      if D is TIDLDictionaryDefinition then
+        if WriteForwardClassDef(D as TIDLDictionaryDefinition) then
+          Inc(Result);
+end;
+
+procedure TTypescriptToPas.WriteSequenceDef(aDef : TIDLSequenceTypeDefDefinition);
+
+begin
+  Addln('%s = array of %s;',[GetName(aDef),GetTypeName(aDef.ElementType)])
+end;
+
+
+procedure TTypescriptToPas.WriteUnionDef(aDef : TIDLUnionTypeDefDefinition);
+
+Var
+  S : UTF8String;
+  D : TIDLDefinition;
+begin
+  S:='';
+  For D in adef.Union do
+    begin
+    if (S<>'') then
+      S:=S+', ';
+    S:=S+(D as TIDLTypeDefDefinition).TypeName;
+    end;
+  Comment('Union of '+S);
+  AddLn('%s = JSValue; ',[GetName(aDef)])
+end;
+
+
+procedure TTypescriptToPas.WritePromiseDef(aDef : TIDLPromiseTypeDefDefinition);
+
+begin
+  AddLn('%s = TJSPromise;',[GetName(aDef)]);
+end;
+
+procedure TTypescriptToPas.WriteAliasTypeDef(aDef : TJSTypeDeclaration);
+
+Var
+  TN : String;
+
+begin
+  TN:=GetTypeName((aDef.TypeDef as TJSTypeReference).Name,True);
+  AddLn('%s = %s;',[GetName(aDef),TN]);
+end;
+
+function TTypescriptToPas.WriteTypeDef(aDef: TJSTypeDeclaration): Boolean;
+
+begin
+  Result:=True;
+  if ADef is TIDLSequenceTypeDefDefinition then
+    WriteSequenceDef(aDef as TIDLSequenceTypeDefDefinition)
+  else if ADef is TIDLUnionTypeDefDefinition then
+    WriteUnionDef(aDef as TIDLUnionTypeDefDefinition)
+  else if ADef is TIDLPromiseTypeDefDefinition then
+    WritePromiseDef(aDef as TIDLPromiseTypeDefDefinition)
+  else if ADef is TIDLRecordDefinition then
+    WriteRecordDef(aDef as TIDLRecordDefinition)
+  else
+    WriteAliasTypeDef(aDef);
+end;
+
+function TTypescriptToPas.WriteRecordDef(aDef: TIDLRecordDefinition): Boolean;
+
+Var
+  KT,VT : String;
+
+begin
+  Result:=True;
+  KT:=GetTypeName(aDef.KeyType);
+  VT:=GetTypeName(aDef.ValueType);
+  AddLn('%s = Class(TJSObject)',[GetName(aDef)]);
+  AddLn('private');
+  Indent;
+  AddLn('function GetValue(aKey: %s): %s; external name ''[]'';',[KT,VT]);
+  AddLn('procedure SetValue(aKey: %s; const AValue: %s); external name ''[]'';',[KT,VT]);
+  undent;
+  AddLn('public');
+  Indent;
+  AddLn('property Values[Name: %s]: %s read GetProperties write SetProperties; default;',[KT,VT]);
+  undent;
+  AddLn('end;');
+end;
+
+
+function TTypescriptToPas.WriteEnumDef(aDef: TIDLEnumDefinition): Boolean;
+
+begin
+  Result:=True;
+  AddLn('%s = String;',[GetName(aDef)]);
+end;
+
+function TTypescriptToPas.WriteEnumDefs(aList: TIDLDefinitionList): Integer;
+
+Var
+  D : TIDLDefinition;
+  ED : TIDLEnumDefinition absolute D;
+
+begin
+  Result:=0;
+  EnsureSection(csType);
+  for D in aList do
+    if D is TIDLEnumDefinition then
+      if WriteEnumDef(ED) then
+        Inc(Result);
+end;
+
+function TTypescriptToPas.GetArguments(aList: TIDLDefinitionList;
+  ForceBrackets: Boolean): String;
+
+Var
+  I : TIDLDefinition;
+  A : TIDLArgumentDefinition absolute I;
+  Arg : string;
+
+begin
+  Result:='';
+  For I in aList do
+    begin
+    Arg:=GetName(A);
+    Arg:=Arg+' : '+GetTypeName(A.ArgumentType);
+    if Result<>'' then
+      Result:=Result+'; ';
+    Result:=Result+Arg;
+    end;
+  if (Result<>'') or ForceBrackets then
+    Result:='('+Result+')';
+end;
+
+Type
+  // A partial argument list is a list which has been generated for a optional argument.
+  // Additional arguments can never be added to a partial list...
+  TIDLPartialDefinitionList = Class(TIDLDefinitionList);
+
+function TTypescriptToPas.CloneNonPartialArgumentList(aList: TFPObjectlist;
+  ADest: TFPObjectlist; AsPartial: Boolean): integer;
+
+Var
+  I,J : Integer;
+  CD : TIDLDefinition;
+  DL,CL : TIDLDefinitionList;
+
+begin
+  Result:=0;
+  if ADest=Nil then
+    ADest:=aList;
+  I:=aList.Count-1;
+  While (I>=0) do
+    begin
+    DL:=TIDLDefinitionList(alist[i]);
+    if Not (DL is TIDLPartialDefinitionList) then
+      begin
+      Inc(Result);
+      if AsPartial then
+        CL:=TIDLPartialDefinitionList.Create(Nil,True)
+      else
+        CL:=TIDLDefinitionList.Create(Nil,True);
+      aDest.Add(CL);
+      For J:=0 to DL.Count-1 do
+        begin
+        CD:=(DL.Definitions[J] as TIDLArgumentDefinition).Clone(Nil);
+        CL.Add(CD);
+        AllocatePasName(CD);
+        end;
+      end;
+    Dec(I);
+    end;
+end;
+
+procedure TTypescriptToPas.AddArgumentToOverloads(aList: TFPObjectlist; AName,ATypeName : String);
+
+Var
+  I : Integer;
+  CD : TIDLArgumentDefinition;
+  DL : TIDLDefinitionList;
+
+begin
+  For I:=0 to aList.Count-1 do
+    begin
+    DL:=TIDLDefinitionList(alist[i]);
+    if Not (DL is TIDLPartialDefinitionList) then
+      begin
+      CD:=TIDLArgumentDefinition.Create(Nil,aName);
+      CD.ArgumentType:=TIDLTypeDefDefinition.Create(CD,'');
+      CD.ArgumentType.TypeName:=aTypeName;
+      DL.Add(CD);
+      AllocatePasName(cd,'');
+      end;
+    end;
+end;
+
+procedure TTypescriptToPas.AddArgumentToOverloads(aList: TFPObjectlist; adef: TIDLArgumentDefinition);
+
+Var
+  I : Integer;
+  CD : TIDLDefinition;
+  DL : TIDLDefinitionList;
+
+begin
+  For I:=0 to aList.Count-1 do
+    begin
+    DL:=TIDLDefinitionList(alist[i]);
+    if Not (DL is TIDLPartialDefinitionList) then
+      begin
+      CD:=aDef.Clone(Nil);
+      DL.Add(CD);
+      if aDef.Data<>Nil then
+        CD.Data:=CreatePasName(TPasData(aDef.Data).PasName)
+      else
+        AllocatePasName(cd,'');
+      end;
+    end;
+end;
+
+procedure TTypescriptToPas.AddUnionOverloads(aList: TFPObjectlist; AName : String; UT : TIDLUnionTypeDefDefinition);
+
+Var
+  L,L2 : TFPObjectList;
+  I,J : Integer;
+  D : TIDLDefinitionList;
+  Dups : TStringList;
+
+begin
+  L2:=Nil;
+  Dups:=TStringList.Create;
+  Dups.Sorted:=True;
+  Dups.Duplicates:=dupIgnore;
+  L:=TFPObjectList.Create(False);
+  try
+    L2:=TFPObjectList.Create(False);
+    // Collect non partial argument lists
+    for I:=0 to AList.Count-1 do
+      begin
+      D:=TIDLDefinitionList(alist[i]);
+      if Not (D is TIDLPartialDefinitionList) then
+        L.Add(D);
+      end;
+    // Collect unique pascal types. Note that this can reduce the list to 1 element...
+    For I:=0 to UT.Union.Count-1 do
+      Dups.AddObject(GetTypeName(UT.Union[I] as TIDLTypeDefDefinition),UT.Union[I]);
+    // First, clone list and add argument to cloned lists
+    For I:=1 to Dups.Count-1 do
+      begin
+      // Clone list
+      CloneNonPartialArgumentList(L,L2,False);
+      // Add argument to cloned list
+      AddArgumentToOverloads(L2,aName,Dups[i]);
+      // Add overloads to original list
+      For J:=0 to L2.Count-1 do
+        aList.Add(L2[J]);
+      L2.Clear;
+      end;
+    // Add first Union to original list
+    AddArgumentToOverloads(L,aName,Dups[0]);
+  finally
+    Dups.Free;
+    L2.Free;
+    L.Free;
+  end;
+end;
+
+function TTypescriptToPas.CheckUnionTypeDefinition(D: TIDLDefinition
+  ): TIDLUnionTypeDefDefinition;
+
+begin
+  Result:=Nil;
+  If (D is TIDLUnionTypeDefDefinition) then
+    Result:=D as TIDLUnionTypeDefDefinition
+  else
+    begin
+    D:=Context.FindDefinition((D as TIDLTypeDefDefinition).TypeName);
+    if (D is TIDLUnionTypeDefDefinition) then
+      Result:=D as TIDLUnionTypeDefDefinition
+    end
+end;
+
+procedure TTypescriptToPas.AddOverloads(aList: TFPObjectlist;
+  adef: TIDLFunctionDefinition; aIdx: Integer);
+
+Var
+  Arg : TIDLArgumentDefinition;
+  D : TIDLDefinition;
+  UT : TIDLUnionTypeDefDefinition;
+
+begin
+ if aIdx>=ADef.Arguments.Count then
+    exit;
+  Arg:=ADef.Argument[aIdx];
+  if Arg.IsOptional then
+    CloneNonPartialArgumentList(aList);
+  // Add current to list.
+  D:=Arg.ArgumentType;
+  UT:=Nil;
+  if coExpandUnionTypeArgs in Options then
+    UT:=CheckUnionTypeDefinition(D);
+  if UT=Nil then
+    AddArgumentToOverloads(aList,Arg)
+  else
+    AddUnionOverLoads(aList,Arg.Name,UT);
+  AddOverloads(aList,aDef,aIdx+1);
+end;
+
+function TTypescriptToPas.GetOverloads(aDef: TIDLFunctionDefinition): TFPObjectlist;
+
+begin
+  Result:=TFPObjectList.Create;
+  try
+    Result.Add(TIDLDefinitionList.Create(Nil,True));
+    AddOverloads(Result,adef,0);
+  except
+    Result.Free;
+    Raise;
+  end;
+end;
+
+function TTypescriptToPas.WriteFunctionTypeDefinition(aDef: TIDLFunctionDefinition): Boolean;
+
+Var
+  FN,RT,Args : String;
+
+begin
+  Result:=True;
+  FN:=GetName(aDef);
+  RT:=GetTypeName(aDef.ReturnType,False);
+  if (RT='void') then
+    RT:='';
+  Args:=GetArguments(aDef.Arguments,False);
+  if (RT='') then
+    AddLn('%s = Procedure %s;',[FN,Args])
+  else
+    AddLn('%s = function %s: %s;',[FN,Args,RT])
+end;
+
+function TTypescriptToPas.WriteFunctionDefinition(aDef: TIDLFunctionDefinition): Boolean;
+
+Var
+  FN,RT,Suff,Args : String;
+  Overloads : TFPObjectList;
+  I : Integer;
+
+begin
+  Result:=True;
+  if not (foConstructor in aDef.Options) then
+    begin
+    FN:=GetName(aDef);
+    if FN<>aDef.Name then
+      Suff:=Format('; external name ''%s''',[aDef.Name]);
+    RT:=GetTypeName(aDef.ReturnType,False);
+    if (RT='void') then
+      RT:='';
+    end
+  else
+    FN:='New';
+  Overloads:=GetOverloads(ADef);
+  try
+    for I:=0 to aDef.Arguments.Count-1 do
+      if aDef.Argument[i].HasEllipsis then
+        Suff:='; varargs';
+    if Overloads.Count>1 then
+      Suff:=Suff+'; overload';
+    For I:=0 to Overloads.Count-1 do
+      begin
+      Args:=GetArguments(TIDLDefinitionList(Overloads[i]),False);
+      if (RT='') then
+        begin
+        if not (foConstructor in aDef.Options) then
+          AddLn('Procedure %s%s%s;',[FN,Args,Suff])
+        else
+          AddLn('constructor %s%s%s;',[FN,Args,Suff]);
+        end
+      else
+        AddLn('function %s%s: %s%s;',[FN,Args,RT,Suff])
+      end;
+  finally
+    Overloads.Free;
+  end;
+end;
+
+function TTypescriptToPas.WriteCallBackDefs(aList: TIDLDefinitionList): Integer;
+
+Var
+  D : TIDLDefinition;
+  FD : TIDLFunctionDefinition absolute D;
+
+begin
+  Result:=0;
+  EnsureSection(csType);
+  for D in aList do
+    if D is TIDLFunctionDefinition then
+      if (foCallBack in FD.Options) then
+         if WriteFunctionTypeDefinition(FD) then
+           Inc(Result);
+end;
+
+function TTypescriptToPas.WriteDictionaryDefs(aList: TIDLDefinitionList): Integer;
+
+Var
+  D : TIDLDefinition;
+  DD : TIDLDictionaryDefinition absolute D;
+
+begin
+  Result:=0;
+  EnsureSection(csType);
+  for D in aList do
+    if D is TIDLDictionaryDefinition then
+      if not TIDLDictionaryDefinition(D).IsPartial then
+        if WriteDictionaryDef(DD) then
+          Inc(Result);
+end;
+
+function TTypescriptToPas.WriteInterfaceDefs(aList: TIDLDefinitionList): Integer;
+
+Var
+  D : TIDLDefinition;
+  ID : TIDLInterfaceDefinition absolute D;
+
+begin
+  Result:=0;
+  EnsureSection(csType);
+  for D in aList do
+    if D is TIDLInterfaceDefinition then
+      if not TIDLInterfaceDefinition(D).IsPartial then
+        if WriteInterfaceDef(ID) then
+          Inc(Result);
+end;
+*)
+
+procedure TTypescriptToPas.Getoptions(L : TStrings);
+
+Var
+  S : String;
+  I : Integer;
+
+begin
+  L.Add('Automatically generated file by '+ClassName+' on '+FormatDateTime('yyyy-mm-dd hh:nn:ss',Now));
+  L.Add('');
+  L.Add('Used command-line options : ');
+  For I:=1 to ParamCount do
+    L.Add(ParamStr(i));
+  L.Add('');
+  L.Add('Command-line options translate to: ');
+  L.Add('');
+  S:=SetToString(PtypeInfo(TypeInfo(TConversionOptions)),Integer(OPtions),True);
+  L.Add('Options : '+S);
+  L.Add('Keyword prefix : '+KeywordPrefix);
+  L.Add('Keyword suffix : '+KeywordSuffix);
+  L.Add('Class prefix : '+ClassPrefix);
+  L.Add('Class suffix : '+ClassSuffix);
+  L.Add('Field prefix : '+FieldPrefix);
+  Str(ECMAversion,S);
+  L.Add('ECMALversion : '+S);
+  if TypeAliases.Count>0 then
+    begin
+    L.Add('Type aliases:');
+    L.AddStrings(Self.TypeAliases);
+    end;
+end;
+
+procedure TTypescriptToPas.AddOptionsToHeader;
+
+Var
+  L : TStrings;
+begin
+  L:=TStringList.Create;
+  try
+    GetOptions(L);
+    Comment(L);
+  finally
+    L.Free;
+  end;
+end;
+
+procedure TTypescriptToPas.WriteIncludeInterfaceCode;
+
+Var
+  S : String;
+
+begin
+  For S in IncludeInterfaceCode do
+    Addln(S);
+end;
+
+constructor TTypescriptToPas.Create(Aowner: TComponent);
+begin
+  inherited Create(Aowner);
+  ECMaVersion:=ecma2021;
+  FieldPrefix:='F';
+  ClassPrefix:='T';
+  ClassSuffix:='';
+  Switches.Add('modeswitch externalclass');
+  FTypeAliases:=TStringList.Create;
+  FPasNameList:=TFPObjectList.Create(True);
+  FAutoTypes:=TStringList.Create;
+  FIncludeInterfaceCode:=TStringList.Create;
+  FIncludeImplementationCode:=TStringList.Create;
+end;
+
+
+destructor TTypescriptToPas.Destroy;
+begin
+  FreeAndNil(FElements);
+  FreeAndNil(FIncludeInterfaceCode);
+  FreeAndNil(FIncludeImplementationCode);
+  FreeAndNil(FAutoTypes);
+  FreeAndNil(FTypeAliases);
+  FreeAndNil(FPasNameList);
+  inherited Destroy;
+end;
+
+
+procedure TTypescriptToPas.WriteVariable(aVar : TJSVarDeclaration);
+
+Var
+  Src,aPasName,aTypeName,aExportName : String;
+
+begin
+  aPasName:=GetName(aVar);
+  aExportName:=aVar.Name;
+  aTypeName:=GetTypeName(aVar.Typed,False);
+  Src:=aPasName + ' : '+aTypeName+';';
+  if aExportName<>aPasName then
+    Src:=Src+' external name '''+aExportName+''';';
+  AddLn(Src);
+end;
+
+procedure TTypescriptToPas.WriteVariables(Vars : TJSElementNodes);
+
+Var
+  I : Integer;
+
+begin
+  For I:=0 to Vars.Count-1 do
+    WriteVariable(Vars.Nodes[i].Node as TJSVarDeclaration);
+end;
+
+procedure TTypescriptToPas.WritePascal;
+
+Var
+  SourceElements : TJSSourceElements;
+
+begin
+  SourceElements:=FElements.A as TJSSourceElements;
+  if Not IsRaw then
+    begin
+    CreateUnitClause;
+    CreateHeader;
+    if coaddOptionsToheader in Options then
+      AddOptionsToHeader;
+    end;
+  if SourceElements.Types.Count>0 then
+    begin
+    EnsureSection(csType);
+    Indent;
+    WriteTypeDefs(SourceElements.Types);
+    {
+    WriteForwardClassDefs(Context.Definitions);
+    WriteEnumDefs(Context.Definitions);
+    WriteCallbackDefs(Context.Definitions);
+    WriteDictionaryDefs(Context.Definitions);
+    WriteInterfaceDefs(Context.Definitions);
+    }
+    Undent;
+    end;
+  if SourceElements.Vars.Count>0 then
+    begin
+    EnsureSection(csVar);
+    WriteVariables(SourceElements.Vars);
+    end;
+  if not IsRaw then
+    begin
+    WriteIncludeInterfaceCode;
+    Addln('');
+    AddLn('implementation');
+    WriteImplementation;
+    AddLn('end.');
+    end;
+  if OutputFileName<>'' then
+    Source.SaveToFile(OutputFileName);
+end;
+
+function TTypescriptToPas.BaseUnits: String;
+
+begin
+  Result:='SysUtils, JS'
+end;
+
+function TTypescriptToPas.CreatePasName(const aOriginal: jsBase.TJSString; const aName: String): TPasData;
+
+begin
+  Result:=TPasData.Create(aOriginal,EscapeKeyWord(aName));
+  FPasNameList.Add(Result);
+end;
+
+function TTypescriptToPas.AllocatePasName(D: TJSElement; ParentName: String): TPasData;
+
+Var
+  Org : TJSString;
+  CN : String;
+  CD : TJSClassDeclaration absolute D;
+  ID : TJSInterfaceDeclaration absolute D;
+  VD : TJSVarDeclaration absolute D;
+  TD : TJSTypeDeclaration absolute D;
+
+begin
+  Result:=Nil;
+  if D Is TJSClassDeclaration then
+    begin
+    Org:=CD.Name;
+    CN:=ClassPrefix+Org+ClassSuffix;
+    Result:=CreatePasname(Org,CN);
+    AllocatePasNames(CD.members,CD.Name);
+    end
+  else if D Is TJSInterfaceDeclaration then
+    begin
+    Org:=ID.Name;
+    CN:=ClassPrefix+Org+ClassSuffix;
+    Result:=CreatePasname(Org,CN);
+    AllocatePasNames(ID.Values,EscapeKeyWord(UTF8Encode(ID.Name)));
+    end
+  else if D Is TJSVarDeclaration then
+    begin
+    Org:=VD.Name;
+    Result:=CreatePasName(Org, EscapeKeyWord(UTF8Encode(Org)));
+    end
+  else if D Is TJSTypeDeclaration then
+    begin
+    Org:=TD.Name;
+    Result:=CreatePasName(Org, EscapeKeyWord('T'+UTF8Encode(Org)));
+    end
+  else
+    Raise ETSToPas.CreateFmt('Unsupported type to get name from: "%s"',[D.ClassName]);
+  D.Data:=Result;
+  if Verbose and (Result<>Nil) and (Result.PasName<>UTF8Encode(Org)) then
+    begin
+    if (ParentName<>'') then
+      ParentName:=ParentName+'.';
+    DoLog('Renamed %s to %s',[ParentName+UTF8Encode(Org),TPasData(D.Data).PasName]);
+    end;
+end;
+
+procedure TTypescriptToPas.SetTypeAliases(AValue: TStrings);
+begin
+  if FTypeAliases=AValue then Exit;
+  FTypeAliases.Assign(AValue);
+end;
+
+procedure TTypescriptToPas.SetIncludeInterfaceCode(AValue: TStrings);
+begin
+  if FIncludeInterfaceCode=AValue then Exit;
+  FIncludeInterfaceCode.Assign(AValue);
+end;
+
+procedure TTypescriptToPas.SetIncludeImplementationCode(AValue: TStrings);
+begin
+  if FIncludeImplementationCode=AValue then Exit;
+  FIncludeImplementationCode.Assign(AValue);
+end;
+
+function TTypescriptToPas.GetIsRaw: Boolean;
+begin
+  Result:=coRaw in Options;
+end;
+
+procedure TTypescriptToPas.AllocatePasNames(aList : TJSElementNodes; ParentName: String = '');
+
+Var
+  I : Integer;
+
+begin
+  For I:=0 to aList.Count-1 do
+    AllocatePasName(aList.Nodes[i].Node,ParentName);
+end;
+
+procedure TTypescriptToPas.AllocatePasNames(aList : TJSSourceElements; ParentName: String = '');
+
+Var
+  I : Integer;
+
+begin
+  For I:=0 to aList.Types.Count-1 do
+    AllocatePasNames(aList.Types,ParentName);
+  For I:=0 to aList.Vars.Count-1 do
+    AllocatePasNames(aList.Vars,ParentName);
+end;
+
+
+procedure TTypescriptToPas.EnsureUniqueNames(ML: TJSSourceElements);
+begin
+
+end;
+
+procedure TTypescriptToPas.ProcessDefinitions;
+
+begin
+  AllocatePasNames((FElements.A as TJSSourceElements));
+end;
+
+procedure TTypescriptToPas.Execute;
+
+begin
+  FContext:=CreateContext;
+  try
+    if Assigned(TypeAliases) then
+      FContext.AddAliases(TypeAliases);
+    Parse;
+    if Verbose then
+      DoLog('Parsed %d definitions.',[]);
+    ProcessDefinitions;
+    WritePascal;
+  finally
+    FreeAndNil(FContext);
+  end;
+end;
+
+Function TTypescriptToPas.GetArrayTypeAsString(aTypeDef : TJSArrayTypeDef; asPascal,asSubType : Boolean) : String;
+
+begin
+  Result:=GetTypeAsString(aTypeDef,asPascal,True);
+  if coGenericArrays in Options then
+    Result:='TArray<'+Result+'>'
+  else
+    Result:='array of '+Result;
+  if AsSubType then
+    Result:='('+Result+')'
+end;
+
+
+Function TTypescriptToPas.GetTypeAsString(aType : TJSTypeDef; asPascal,asSubType : Boolean) : String;
+
+begin
+  Result:='';
+  if aType is TJSTypeReference then
+    Result:=GetAliasTypeAsString(TJSTypeReference(aType),asPascal,asSubType)
+  else if aType is TJSUnionTypeDef then
+    Result:=GetUnionTypeAsString(TJSUnionTypeDef(aType),asPascal,asSubType)
+  else if aType is TJSIntersectionTypeDef then
+    Result:=GetIntersectionTypeAsString(TJSIntersectionTypeDef(aType),asPascal,asSubType)
+  else if aType is TJSArrayTypeDef then
+    Result:=GetArrayTypeAsString(TJSArrayTypeDef(aType),asPascal,asSubType)
+end;
+
+Function TTypescriptToPas.GetUnionTypeAsString(aTypeDef : TJSUnionTypeDef; asPascal,asSubType : Boolean) : String;
+
+Var
+  I : Integer;
+
+begin
+  Result:='';
+  For I:=0 to aTypeDef.TypeCount-1 do
+    begin
+    if Result<>'' then
+      Result:=Result+'&';
+    GetTypeAsString(aTypeDef.Types[I],asPascal,True);
+    end;
+  if AsSubType then
+    Result:='('+Result+')';
+end;
+
+Function TTypescriptToPas.GetIntersectionTypeAsString(aTypeDef : TJSIntersectionTypeDef; asPascal,asSubType : Boolean) : String;
+
+Var
+  I : Integer;
+
+begin
+  Result:='';
+  For I:=0 to aTypeDef.TypeCount-1 do
+    begin
+    if Result<>'' then
+      Result:=Result+'|';
+    GetTypeAsString(aTypeDef.Types[I],asPascal,True);
+    end;
+  if AsSubType then
+    Result:='('+Result+')';
+end;
+
+Procedure TTypescriptToPas.WriteUnionTypeDef(const aPasName : string; const aOrgName : jsBase.TJSString; aTypeParams: TJSElementNodes;aTypeDef : TJSUnionTypeDef);
+
+var
+  TN, gen, genparams: String;
+
+begin
+  TN:='jsvalue';
+  genparams:=GetGenericParams(aTypeParams);
+  if (genparams<>'') then
+    gen:='generic ';
+  AddLn('%s%s%s = %s; // %s',[gen,aPasName,genparams,TN,GetTypeAsString(aTypeDef,False,False)]);
+end;
+
+
+Procedure TTypescriptToPas.WriteIntersectionTypeDef(const aPasName : string; const aOrgName : jsBase.TJSString; aTypeParams: TJSElementNodes;aTypeDef : TJSIntersectionTypeDef);
+
+var
+  TN, gen, genparams: String;
+
+begin
+  TN:='jsvalue';
+  genparams:=GetGenericParams(aTypeParams);
+  if (genparams<>'') then
+    gen:='generic ';
+  AddLn('%s%s%s = %s; // Intersection type: %s',[gen,aPasName,genparams,TN,GetTypeAsString(aTypeDef,False,false)]);
+end;
+
+Procedure TTypescriptToPas.WriteArrayTypeDef(const aPasName : string; const aOrgName : jsBase.TJSString; aTypeParams: TJSElementNodes;aTypeDef : TJSArrayTypeDef);
+
+var
+  TN, arr,gen, genparams: String;
+
+begin
+  TN:='jsvalue';
+  genparams:=GetGenericParams(aTypeParams);
+  if (genparams<>'') then
+    gen:='generic ';
+  arr:=GetArrayTypeAsString(aTypeDef,True,False);
+  AddLn('%s%s%s = %s;',[gen,aPasName,genparams,arr]);
+end;
+
+
+Procedure TTypescriptToPas.WriteTypeDef(const aPasName : string; const aOrgName : jsBase.TJSString; aTypeParams: TJSElementNodes; aTypeDef : TJSTypeDef);
+
+begin
+  If aTypeDef is TJSTypeReference then
+    WriteAliasTypeDef(aPasName,aOrgName,aTypeParams,TJSTypeReference(aTypeDef))
+  else if aTypeDef is TJSUnionTypeDef then
+    WriteUnionTypeDef(aPasName,aOrgName,aTypeParams,TJSUnionTypeDef(aTypeDef))
+  else if aTypeDef is TJSIntersectionTypeDef then
+    WriteIntersectionTypeDef(aPasName,aOrgName,aTypeParams,TJSIntersectionTypeDef(aTypeDef))
+  else if aTypeDef is TJSArrayTypeDef then
+    WriteArrayTypeDef(aPasName,aOrgName,aTypeParams,TJSArrayTypeDef(aTypeDef))
+end;
+
+Procedure TTypescriptToPas.WriteTypeDefs(Types: TJSElementNodes);
+
+Var
+  I : Integer;
+  N : TJSElement;
+  Decl : TJSTypeDeclaration absolute N;
+  aName : String;
+
+begin
+  EnsureSection(csType);
+  for I:=0 to Types.Count-1 do
+    begin
+    N:=Types[0].Node;
+    if N is TJSTypeDeclaration then
+      begin
+      aName:=GetName(Decl);
+      WriteTypeDef(aName, Decl.Name, Decl.TypeParams, Decl.TypeDef);
+      end;
+    end;
+end;
+
+
+end.
+

+ 335 - 79
packages/fcl-js/tests/tcparser.pp

@@ -11,17 +11,20 @@ type
 
   { TTestJSParser }
 
-  TTestJSParser= class(TTestCase)
+  { TTestBaseJSParser }
+
+  TTestBaseJSParser = class(TTestCase)
   Private
     FSource : TStringStream;
     FParser : TJSParser;
     FSE : TJSSourceElements;
     FToFree: TJSElement;
   protected
-    procedure SetUp; override; 
+    procedure SetUp; override;
     procedure TearDown; override;
-    Procedure CreateParser(Const ASource : string; aVersion : TECMAVersion = TECMAVersion.ecma5);
-    Procedure CheckClass(E : TJSElement; C : TJSElementClass);
+    Procedure CreateParser(Const ASource : string; aVersion : TECMAVersion = TECMAVersion.ecma5; aIsTypeScript : Boolean = False);
+    Function CheckClass(E : TJSElement; C : TJSElementClass; Const aMsg : String = '') : TJSElement;
+    Function CheckClass(Const aMsg : String; aExpectedClass : TJSElementClass; aActual : TJSElement) : TJSElement;
     Procedure AssertEquals(Const AMessage : String; Expected, Actual : TJSToken); overload;
     Procedure AssertEquals(Const AMessage : String; Expected, Actual : TJSType); overload;
     Procedure AssertEquals(Const AMessage : String; Expected, Actual : TJSVarType); overload;
@@ -30,9 +33,25 @@ type
     Function  GetVars : TJSElementNodes;
     Function  GetStatements : TJSElementNodes;
     Function  GetFunctions : TJSElementNodes;
+    Function  GetFirstFunction : TJSFunctionDeclarationStatement;
+    Function  GetClasses : TJSElementNodes;
+    Function  GetFirstClass : TJSClassDeclaration;
     Function  GetFirstStatement : TJSElement;
     Function  GetFirstVar : TJSElement;
+    Function  GetModules : TJSElementNodes;
+    Function  GetFirstModule : TJSModuleDeclaration;
+    Function  GetNameSpaces : TJSElementNodes;
+    Function  GetFirstNameSpace : TJSNamespaceDeclaration;
+    Function  GetTypes : TJSElementNodes;
+    Function  GetFirstType : TJSTypeDeclaration;
+    Function  GetEnums : TJSElementNodes;
+    Function  GetFirstEnum: TJSEnumDeclaration;
     Function  GetExpressionStatement : TJSExpressionStatement;
+    Function  GetFirstInterface : TJSInterfaceDeclaration;
+  end;
+
+  TTestJSParser= class(TTestBaseJSParser)
+  private
   published
     procedure TestEmpty;
     procedure TestSimple;
@@ -98,10 +117,12 @@ type
     procedure TestAwaitFunctionCallNoArgs;
     procedure TestFunctionCallOneArg;
     procedure TestFunctionCallTwoArgs;
+    procedure TestObjectGeneratorFunction;
     procedure TestArrayExpressionNumericalArgs;
     procedure TestArrayExpressionStringArgs;
     procedure TestArrayExpressionIdentArgs;
     Procedure TestVarDeclarationSimple;
+    Procedure TestVarDeclarationInit;
     Procedure TestLetDeclarationSimple;
     procedure TestVarDeclarationDouble;
     procedure TestVarDeclarationSimpleInit;
@@ -164,13 +185,130 @@ type
     Procedure TestExportDefaultAssignment;
     Procedure TestExportDefaultFunction;
     Procedure TestExportDefaultAsyncFunction;
+    Procedure TestClass;
+    Procedure TestClassExtends;
+    Procedure TestClassWithMethod;
+    procedure TestClassExpression;
+    procedure TestLetClassExpression;
   end;
 
 implementation
 
 uses typinfo;
 
-Procedure TTestJSParser.AssertEquals(Const AMessage: String; Expected,
+{ ----------------------------------------------------------------------
+  TTestBaseJSParser
+  ----------------------------------------------------------------------}
+
+procedure TTestBaseJSParser.SetUp;
+begin
+  FParser:=Nil;
+  FSource:=Nil;
+end;
+
+procedure TTestBaseJSParser.TearDown;
+begin
+  FreeAndNil(FToFree);
+  FreeAndNil(FParser);
+  FReeAndNil(FSource);
+end;
+
+Procedure TTestBaseJSParser.CreateParser(Const ASource: string; aVersion : TECMAVersion = TECMAVersion.ecma5; aIsTypeScript : Boolean = False);
+begin
+  FSource:=TStringStream.Create(ASource);
+  FParser:=TJSParser.Create(FSource,aVersion,aIsTypescript);
+end;
+
+function TTestBaseJSParser.CheckClass(E: TJSElement; C: TJSElementClass; const aMsg: String): TJSElement;
+begin
+  AssertNotNull(aMsg+': Not null element',E);
+  AssertNotNull(aMsg+': Not null class',C);
+  AssertEquals(aMsg,C,E.ClassType);
+  Result:=E;
+end;
+
+function TTestBaseJSParser.CheckClass(const aMsg: String; aExpectedClass: TJSElementClass; aActual: TJSElement): TJSElement;
+begin
+  Result:=CheckClass(aActual,aExpectedClass,aMsg);
+end;
+
+procedure TTestBaseJSParser.AssertEquals(const AMessage: String; Expected, Actual: TJSToken);
+Var
+  NE,NA : String;
+
+begin
+  NE:=GetEnumName(TypeInfo(TJSToken),Ord(Expected));
+  NA:=GetEnumName(TypeInfo(TJSToken),Ord(Actual));
+  AssertEquals(AMessage,NE,NA);
+end;
+
+Function TTestBaseJSParser.GetSourceElements: TJSSourceElements;
+
+Var
+  E : TJSElement;
+  FB : TJSFunctionBody;
+
+begin
+  If Not Assigned(FSE) then
+    begin
+    AssertNotNull('Parser assigned',FParser);
+    E:=FParser.Parse;
+    CheckClass(E,TJSFunctionBody);
+    FB:=TJSFunctionBody(E);
+    AssertNotNull(FB.A);
+    CheckClass(FB.A,TJSSourceElements);
+    FSE:=TJSSourceElements(FB.A);
+    FToFree:=E;
+    end;
+  Result:=FSE;
+end;
+
+Function TTestBaseJSParser.GetVars: TJSElementNodes;
+begin
+  Result:=GetSourceElements.Vars;
+end;
+
+Function TTestBaseJSParser.GetStatements: TJSElementNodes;
+begin
+  Result:=GetSourceElements.Statements;
+end;
+
+Function TTestBaseJSParser.GetFunctions: TJSElementNodes;
+begin
+  Result:=GetSourceElements.Functions;
+end;
+
+function TTestBaseJSParser.GetFirstFunction: TJSFunctionDeclarationStatement;
+Var
+  aFunctions : TJSElementNodes;
+
+begin
+  aFunctions:=GetFunctions;
+  AssertTrue('Have functions ',aFunctions.Count>0);
+  AssertNotNull('have first function node',aFunctions.Nodes[0].Node);
+  AssertEquals('First function node is function declaration',TJSFunctionDeclarationStatement,aFunctions.Nodes[0].Node.ClassType);
+  Result:=TJSFunctionDeclarationStatement(aFunctions.Nodes[0].Node);
+end;
+
+function TTestBaseJSParser.GetClasses: TJSElementNodes;
+begin
+  Result:=GetSourceElements.Classes;
+end;
+
+function TTestBaseJSParser.GetFirstClass: TJSClassDeclaration;
+
+Var
+  aClasses : TJSElementNodes;
+
+begin
+  aClasses:=GetClasses;
+  AssertTrue('Have classes ',aClasses.Count>0);
+  AssertNotNull('have first class node',aClasses.Nodes[0].Node);
+  AssertEquals('First class node is class declaration',TJSClassDeclaration,aClasses.Nodes[0].Node.ClassType);
+  Result:=TJSClassDeclaration(aClasses.Nodes[0].Node);
+end;
+
+Procedure TTestBaseJSParser.AssertEquals(Const AMessage: String; Expected,
   Actual: TJSType);
 
 Var
@@ -182,7 +320,7 @@ begin
   AssertEquals(AMessage,NE,NA);
 end;
 
-procedure TTestJSParser.AssertEquals(const AMessage: String; Expected, Actual: TJSVarType);
+procedure TTestBaseJSParser.AssertEquals(const AMessage: String; Expected, Actual: TJSVarType);
 
 Var
   NE,NA : String;
@@ -193,7 +331,7 @@ begin
   AssertEquals(AMessage,NE,NA);
 end;
 
-Procedure TTestJSParser.AssertIdentifier(Msg: String; El: TJSElement;
+Procedure TTestBaseJSParser.AssertIdentifier(Msg: String; El: TJSElement;
   Const AName: TJSString);
 
 Var
@@ -208,7 +346,7 @@ begin
   AssertEquals(Msg+'Identifier has correct name',S2,S1);
 end;
 
-Function TTestJSParser.GetFirstStatement: TJSElement;
+Function TTestBaseJSParser.GetFirstStatement: TJSElement;
 
 Var
   E : TJSElementNodes;
@@ -220,7 +358,7 @@ begin
   AssertNotNull('First statement assigned',Result);
 end;
 
-Function TTestJSParser.GetFirstVar: TJSElement;
+Function TTestBaseJSParser.GetFirstVar: TJSElement;
 Var
   E : TJSElementNodes;
 begin
@@ -233,7 +371,76 @@ begin
   AssertNotNull('First variable declaration',Result);
 end;
 
-Function TTestJSParser.GetExpressionStatement: TJSExpressionStatement;
+function TTestBaseJSParser.GetModules: TJSElementNodes;
+begin
+  Result:=GetSourceElements.Modules;
+end;
+
+function TTestBaseJSParser.GetFirstModule: TJSModuleDeclaration;
+Var
+  E : TJSElementNodes;
+
+begin
+  E:=GetModules;
+  AssertNotNull('Have modules',E);
+  AssertEquals('1 statement',1,E.Count);
+  AssertEquals('First module node is module declaration',TJSModuleDeclaration,E.Nodes[0].Node.ClassType);
+  Result:=E.Nodes[0].Node as TJSModuleDeclaration;
+end;
+
+function TTestBaseJSParser.GetNameSpaces: TJSElementNodes;
+begin
+  Result:=GetSourceElements.NameSpaces;
+end;
+
+function TTestBaseJSParser.GetFirstNameSpace: TJSNamespaceDeclaration;
+
+Var
+  E : TJSElementNodes;
+
+begin
+  E:=GetNameSpaces;
+  AssertNotNull('Have namespaces',E);
+  AssertEquals('1 namespace',1,E.Count);
+  AssertEquals('First module node is namespace declaration',TJSNamespaceDeclaration,E.Nodes[0].Node.ClassType);
+  Result:=E.Nodes[0].Node as TJSNamespaceDeclaration;
+end;
+
+function TTestBaseJSParser.GetTypes: TJSElementNodes;
+begin
+  Result:=GetSourceElements.Types;
+end;
+
+function TTestBaseJSParser.GetFirstType: TJSTypeDeclaration;
+Var
+  E : TJSElementNodes;
+
+begin
+  E:=GetTypes;
+  AssertNotNull('Have types',E);
+  AssertEquals('1 type',1,E.Count);
+  AssertEquals('First type node is type declaration',TJSTypeDeclaration,E.Nodes[0].Node.ClassType);
+  Result:=(E.Nodes[0].Node as TJSTypeDeclaration);
+end;
+
+function TTestBaseJSParser.GetEnums: TJSElementNodes;
+begin
+  Result:=GetSourceElements.Enums;
+end;
+
+function TTestBaseJSParser.GetFirstEnum: TJSEnumDeclaration;
+Var
+  E : TJSElementNodes;
+
+begin
+  E:=GetEnums;
+  AssertNotNull('Have enums',E);
+  AssertEquals('1 type',1,E.Count);
+  AssertEquals('First type node is enum declaration',TJSEnumDeclaration,E.Nodes[0].Node.ClassType);
+  Result:=(E.Nodes[0].Node as TJSEnumDeclaration);
+end;
+
+Function TTestBaseJSParser.GetExpressionStatement: TJSExpressionStatement;
 
 Var
   N : TJSElement;
@@ -243,6 +450,27 @@ begin
   Result:=TJSExpressionStatement(N);
 end;
 
+function TTestBaseJSParser.GetFirstInterface: TJSInterfaceDeclaration;
+Var
+  E : TJSElementNodes;
+
+begin
+  E:=GetTypes;
+  AssertNotNull('Have types',E);
+  AssertEquals('1 type',1,E.Count);
+  AssertEquals('First type node is type declaration',TJSInterfaceDeclaration,E.Nodes[0].Node.ClassType);
+  Result:=(E.Nodes[0].Node as TJSInterfaceDeclaration);
+end;
+
+
+
+{ ----------------------------------------------------------------------
+  TTestJSParser
+  ----------------------------------------------------------------------}
+
+
+
+
 
 procedure TTestJSParser.TestSimple;
 
@@ -1564,6 +1792,30 @@ begin
   AssertEquals('Second argument name correct','e',TJSPrimaryExpressionIdent(E).Name);
 end;
 
+procedure TTestJSParser.TestObjectGeneratorFunction;
+Var
+    X : TJSExpressionStatement;
+    SA : TJSSimpleAssignStatement;
+    Obj : TJSObjectLiteral;
+
+begin
+  CreateParser('a = {* g() { } };',MinGeneratorVersion);
+  X:=GetExpressionStatement;
+  AssertNotNull('Expression statement assigned',X.A);
+  CheckClass(X.A,TJSSimpleAssignStatement);
+  SA:=TJSSimpleAssignStatement(X.A);
+  AssertNotNull('Assignment LHS assigned',SA.LHS);
+  CheckClass(SA.LHS,TJSPrimaryExpressionIdent);
+  AssertEquals('Expression LHS name correct', 'a',TJSPrimaryExpressionIdent(SA.LHS).Name);
+  AssertNotNull('Assignment Expression assigned',SA.Expr);
+  AssertEquals('Assignment Expression assigned',TJSObjectLiteral,SA.Expr.ClassType);
+  Obj:=TJSObjectLiteral(SA.Expr);
+  AssertEquals('Object element count',1,Obj.Elements.Count);
+  AssertEquals('Object element name','g',Obj.Elements[0].Name);
+  AssertEquals('Object element expression',TJSFunctionDeclarationStatement,Obj.Elements[0].Expr.ClassType);
+  AssertTrue('Generator',TJSFunctionDeclarationStatement(Obj.Elements[0].Expr).IsGenerator);
+end;
+
 procedure TTestJSParser.TestArrayExpressionNumericalArgs;
 Var
   X : TJSExpressionStatement;
@@ -1631,6 +1883,23 @@ begin
   AssertNull('No initialization expression', V.Init);
 end;
 
+procedure TTestJSParser.TestVarDeclarationInit;
+Var
+  X : TJSELement;
+  V : TJSVarDeclaration;
+begin
+  CreateParser('var a = 0;');
+  X:=GetFirstVar;
+  AssertNotNull('Variable statement assigned',(X));
+  CheckClass(X,TJSVarDeclaration);
+  V:=TJSVarDeclaration(X);
+  AssertEquals('correct variable type', vtVar, V.VarType);
+  AssertEquals('variable name correct registered', 'a', V.Name);
+  AssertNotNull('initialization expression', V.Init);
+  CheckClass(V.Init,TJSLiteral);
+  AssertEquals('Init value correct', 0, TJSLiteral(V.init).Value.AsNumber);
+end;
+
 procedure TTestJSParser.TestLetDeclarationSimple;
 Var
   X : TJSELement;
@@ -2868,6 +3137,62 @@ begin
   AssertTrue('Async',F.AFunction.IsAsync);
 end;
 
+procedure TTestJSParser.TestClass;
+begin
+  CreateParser('class Rectangle {  } ',MinClassVersion);
+  AssertEquals('class name correct','Rectangle',GetFirstClass.Name);
+  AssertEquals('class extends name correct','',GetFirstClass.Extends);
+end;
+
+procedure TTestJSParser.TestClassExtends;
+begin
+  CreateParser('class Rectangle extends Shape {  function myMethod () { return null; } } ',MinClassVersion);
+  AssertEquals('class name correct','Rectangle',GetFirstClass.Name);
+  AssertEquals('class extends name correct','Shape',GetFirstClass.Extends);
+end;
+
+procedure TTestJSParser.TestClassWithMethod;
+begin
+  CreateParser('class Rectangle {  function myMethod () { return null; } } ',MinClassVersion);
+  AssertEquals('class name correct','Rectangle',GetFirstClass.Name);
+  AssertEquals('class extends name correct','',GetFirstClass.Extends);
+  AssertNotNull('Have members',GetFirstClass.Members);
+  AssertEquals('Have functions',1,GetFirstClass.Members.Functions.Count);
+end;
+
+procedure TTestJSParser.TestClassExpression;
+
+Var
+  X : TJSExpressionStatement;
+  A  : TJSSimpleAssignStatement;
+begin
+  CreateParser('a = class Rectangle {  } ');
+  X:=GetExpressionStatement;
+  CheckClass(X.A,TJSSimpleAssignStatement);
+  A:=TJSSimpleAssignStatement(X.A);
+  AssertNotNull('Have left operand',A.LHS);
+  CheckClass(A.LHS,TJSPrimaryExpressionIdent);
+  AssertEquals('Correct name for assignment LHS ','a',TJSPrimaryExpressionIdent(A.LHS).Name);
+end;
+
+procedure TTestJSParser.TestLetClassExpression;
+Var
+  aVars : TJSVariableStatement;
+  aVarDecl : TJSVarDeclaration;
+  aClass : TJSClassDeclaration;
+begin
+  CreateParser('let a = class Rectangle {  } ',MinClassVersion);
+  AssertEquals('class name correct',TJSVariableStatement, GetFirstStatement.ClassType);
+  aVars:=TJSVariableStatement(GetFirstStatement);
+  AssertEquals('First class node is var declaration',TJSVarDeclaration,aVars.A.ClassType);
+  aVarDecl:=TJSVarDeclaration(aVars.A);
+  AssertNotNull('Var declaration has init',aVarDecl.Init);
+  AssertEquals('Init is class declaration',TJSClassDeclaration,aVarDecl.Init.ClassType);
+  aClass:=TJSClassDeclaration(aVarDecl.Init);
+  AssertEquals('class name correct','Rectangle',aClass.Name);
+end;
+
+
 procedure TTestJSParser.TestExportExportNameFrom;
 
 Var
@@ -3076,75 +3401,6 @@ begin
   end;
 end;
 
-procedure TTestJSParser.SetUp; 
-begin
-  FParser:=Nil;
-  FSource:=Nil;
-end; 
-
-procedure TTestJSParser.TearDown; 
-begin
-  FreeAndNil(FToFree);
-  FreeAndNil(FParser);
-  FReeAndNil(FSource);
-end;
-
-Procedure TTestJSParser.CreateParser(Const ASource: string; aVersion : TECMAVersion = TECMAVersion.ecma5);
-begin
-  FSource:=TStringStream.Create(ASource);
-  FParser:=TJSParser.Create(FSource,aVersion);
-end;
-
-Procedure TTestJSParser.CheckClass(E: TJSElement; C: TJSElementClass);
-begin
-  AssertEquals(C,E.ClassType);
-end;
-
-procedure TTestJSParser.AssertEquals(const AMessage: String; Expected, Actual: TJSToken);
-Var
-  NE,NA : String;
-
-begin
-  NE:=GetEnumName(TypeInfo(TJSToken),Ord(Expected));
-  NA:=GetEnumName(TypeInfo(TJSToken),Ord(Actual));
-  AssertEquals(AMessage,NE,NA);
-end;
-
-Function TTestJSParser.GetSourceElements: TJSSourceElements;
-
-Var
-  E : TJSElement;
-  FB : TJSFunctionBody;
-
-begin
-  If Not Assigned(FSE) then
-    begin
-    AssertNotNull('Parser assigned',FParser);
-    E:=FParser.Parse;
-    CheckClass(E,TJSFunctionBody);
-    FB:=TJSFunctionBody(E);
-    AssertNotNull(FB.A);
-    CheckClass(FB.A,TJSSourceElements);
-    FSE:=TJSSourceElements(FB.A);
-    FToFree:=E;
-    end;
-  Result:=FSE;
-end;
-
-Function TTestJSParser.GetVars: TJSElementNodes;
-begin
-  Result:=GetSourceElements.Vars;
-end;
-
-Function TTestJSParser.GetStatements: TJSElementNodes;
-begin
-  Result:=GetSourceElements.Statements;
-end;
-
-Function TTestJSParser.GetFunctions: TJSElementNodes;
-begin
-  Result:=GetSourceElements.Functions;
-end;
 
 
 initialization

+ 8 - 1
packages/fcl-js/tests/tcscanner.pp

@@ -101,6 +101,7 @@ type
     procedure TestStarEq;
     procedure TestURShift;
     procedure TestURShiftEq;
+    procedure TestArrow;
     procedure TestAwaitECMA5;
     procedure TestAwaitECMA2021;
     procedure TestBreak;
@@ -181,6 +182,7 @@ begin
   FStream:=TStringStream.Create(AInput);
   FLineReader:=TStreamLineReader.Create(Fstream);
   FScanner:=TJSScanner.Create(FLineReader,aVersion);
+  FScanner.IsTypeScript:=False;
   Result:=FScanner;
 end;
 
@@ -484,6 +486,11 @@ begin
   CheckToken(tjsURSHIFTEQ,'>>>=');
 end;
 
+procedure TTestJSScanner.TestArrow;
+begin
+  CheckToken(tjsArrow,'=>');
+end;
+
 procedure TTestJSScanner.TestAwaitECMA5;
 begin
   CheckToken(tjsIdentifier,'await');
@@ -685,7 +692,7 @@ end;
 
 procedure TTestJSScanner.TestLetECMA2021;
 begin
-  CheckToken(tjsLet,'let');
+  CheckToken(tjsLet,'let',ecma2021);
 end;
 
 procedure TTestJSScanner.TestNew;

+ 673 - 0
packages/fcl-js/tests/tctsparser.pp

@@ -0,0 +1,673 @@
+unit tctsparser;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, testregistry, tcparser, jsscanner, jsbase, jstree, jstoken, jsparser;
+
+Type
+
+  { TTestTypeScriptParser }
+
+  TTestTypeScriptParser = Class(TTestBaseJSParser)
+  private
+  Public
+    Procedure StartTS(aSource : String);
+    procedure CheckMembers(M : TJSSourceElements; aVars,aFunctions,aStatements,aClasses,aModules,aNamespaces,aTypes : Integer);
+  Published
+    Procedure TestDeclareModule;
+    Procedure TestDeclareNamespace;
+    Procedure TestDeclareFunction;
+    Procedure TestDeclareTypeAlias;
+    Procedure TestDeclareTypeTypeQuery;
+    Procedure TestDeclareTypeUnion;
+    Procedure TestDeclareTypeTuple;
+    Procedure TestDeclareTypeArray;
+    Procedure TestDeclareTypeArrayOfUnionType;
+    Procedure TestDeclareTypeArrayOfFunctionType;
+    Procedure TestDeclareTypeArrayGeneric;
+    Procedure TestDeclareTypeGeneric;
+    Procedure TestDeclareTypeGenericGeneric;
+    Procedure TestDeclareTypeObject;
+    Procedure TestDeclareTypeArrowFunction;
+    Procedure TestDeclareTypeArrowFunctionShort;
+    Procedure TestDeclareTypeArrowFunctionTwoArgs;
+    Procedure TestDeclareEnum;
+    procedure TestDeclareInterfaceEmpty;
+    procedure TestDeclareAmbientInterfaceEmpty;
+    procedure TestDeclareInterfaceExtendsEmpty;
+    procedure TestDeclareInterfaceExtendsTwoEmpty;
+    procedure TestDeclareInterfaceProperty;
+    procedure TestDeclareInterfaceOptionalProperty;
+    procedure TestDeclareInterfaceUntypedProperty;
+    procedure TestDeclareInterfaceOptionalUntypedProperty;
+    procedure TestDeclareInterfaceMethod;
+    procedure TestDeclareInterfaceMethodNoReturn;
+    procedure TestDeclareInterfaceCallable;
+    procedure TestDeclareInterfaceMethodThisReturn;
+    procedure TestDeclareClassEmpty;
+  end;
+
+implementation
+
+{ TTestTypeScriptParser }
+
+procedure TTestTypeScriptParser.StartTS(aSource: String);
+begin
+  CreateParser(aSource,ecma2021,True);
+end;
+
+procedure TTestTypeScriptParser.CheckMembers(M : TJSSourceElements; aVars, aFunctions, aStatements, aClasses, aModules, aNamespaces,aTypes: Integer);
+begin
+  AssertEquals('functions count',aFunctions,M.Functions.Count);
+  AssertEquals('vars count',aVars,M.Vars.Count);
+  AssertEquals('Statements count',aStatements,M.Statements.Count);
+  AssertEquals('classes count',aClasses,M.Classes.Count);
+  AssertEquals('namespaces count',aNamespaces,M.NameSpaces.Count);
+  AssertEquals('Modules count',aModules,M.Modules.Count);
+  AssertEquals('Types count',aTypes,M.Modules.Count);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareModule;
+
+Var
+  M : TJSModuleDeclaration;
+
+begin
+  StartTS('declare module "a" { }');
+  M:=GetFirstModule;
+  AssertEquals('Name',Unicodestring('a'),M.Name);
+  CheckMembers(M.Members,0,0,0,0,0,0,0);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareNamespace;
+Var
+  N : TJSNamespaceDeclaration;
+
+begin
+  StartTS('declare namespace a { }');
+  N:=GetFirstNamespace;
+  AssertEquals('Name',Unicodestring('a'),N.Name);
+  CheckMembers(N.Members,0,0,0,0,0,0,0);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareFunction;
+
+Var
+  F : TJSFunctionDeclarationStatement;
+
+begin
+  StartTS('declare function a (b : number) : string ;');
+  F:=GetFirstFunction;
+  AssertEquals('Name',Unicodestring('a'),F.AFunction.Name);
+  AssertNull('No body',F.AFunction.Body);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareTypeAlias;
+
+Var
+  T : TJSTypeDeclaration;
+
+begin
+  StartTS('type a = b;');
+  T:=GetFirstType;
+  AssertEquals('Name',Unicodestring('a'),T.Name);
+  AssertNotNull('Have type def',T.TypeDef);
+  AssertEquals('Correct type def class',TJSTypeReference,T.TypeDef.ClassType);
+  AssertEquals('Correct name def class',Unicodestring('b'),TJSTypeReference(T.TypeDef).Name);
+  AssertEquals('Correct name def class',False,TJSTypeReference(T.TypeDef).IsTypeOf);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareTypeTypeQuery;
+Var
+  T : TJSTypeDeclaration;
+
+begin
+  StartTS('type a = typeof b;');
+  T:=GetFirstType;
+  AssertEquals('Name',Unicodestring('a'),T.Name);
+  AssertNotNull('Have type def',T.TypeDef);
+  AssertEquals('Correct type def class',TJSTypeReference,T.TypeDef.ClassType);
+  AssertEquals('Correct name def class',Unicodestring('b'),TJSTypeReference(T.TypeDef).Name);
+  AssertEquals('Correct name def class',True,TJSTypeReference(T.TypeDef).IsTypeOf);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareTypeUnion;
+
+Var
+  T : TJSTypeDeclaration;
+  U : TJSUnionTypeDef;
+  R : TJSTypeReference;
+
+begin
+  StartTS('type a = string | number');
+  T:=GetFirstType;
+  AssertEquals('Name',Unicodestring('a'),T.Name);
+  AssertNotNull('Have type def',T.TypeDef);
+  AssertEquals('Correct type def class',TJSUnionTypeDef,T.TypeDef.ClassType);
+  U:=TJSUnionTypeDef(T.TypeDef);
+  AssertEquals('Correct number of elements',2,U.TypeCount);
+  AssertEquals('Correct type of element 0',TJSTypeReference, U.Types[0].ClassType);
+  R:=U.Types[0] as TJSTypeReference;
+  AssertEquals('Name type 0',Unicodestring('string'),R.Name);
+  AssertEquals('Correct type of element 0',TJSTypeReference, U.Types[1].ClassType);
+  R:=U.Types[1] as TJSTypeReference;
+  AssertEquals('Name type 1',Unicodestring('number'),R.Name);
+
+end;
+
+procedure TTestTypeScriptParser.TestDeclareTypeTuple;
+
+Var
+  T : TJSTypeDeclaration;
+  U : TJSTupleTypeDef;
+  R : TJSTypeReference;
+
+begin
+  StartTS('type a = [string , number]');
+  T:=GetFirstType;
+  AssertEquals('Name',Unicodestring('a'),T.Name);
+  AssertNotNull('Have type def',T.TypeDef);
+  AssertEquals('Correct type def class',TJSTupleTypeDef,T.TypeDef.ClassType);
+  U:=TJSTupleTypeDef(T.TypeDef);
+  AssertEquals('Correct number of elements',2,U.TypeCount);
+  AssertEquals('Correct type of element 0',TJSTypeReference, U.Types[0].ClassType);
+  R:=U.Types[0] as TJSTypeReference;
+  AssertEquals('Name type 0',Unicodestring('string'),R.Name);
+  AssertEquals('Correct type of element 0',TJSTypeReference, U.Types[1].ClassType);
+  R:=U.Types[1] as TJSTypeReference;
+  AssertEquals('Name type 1',Unicodestring('number'),R.Name);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareTypeArray;
+Var
+  T : TJSTypeDeclaration;
+  A : TJSArrayTypeDef;
+  R : TJSTypeReference;
+
+begin
+  StartTS('type a = string[]');
+  T:=GetFirstType;
+  AssertEquals('Name',Unicodestring('a'),T.Name);
+  AssertNotNull('Have type def',T.TypeDef);
+  AssertEquals('Correct type def class',TJSArrayTypeDef,T.TypeDef.ClassType);
+  A:=TJSArrayTypeDef(T.TypeDef);
+  AssertNotNull('have base type',A.BaseType);
+  AssertEquals('Correct type of base type',TJSTypeReference, A.BaseType.ClassType);
+  R:=A.BaseType as TJSTypeReference;
+  AssertEquals('Name base type',Unicodestring('string'),R.Name);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareTypeArrayOfUnionType;
+Var
+  T : TJSTypeDeclaration;
+  A : TJSArrayTypeDef;
+  U : TJSUnionTypeDef;
+  R : TJSTypeReference;
+
+begin
+  StartTS('type a = (string | number)[]');
+  T:=GetFirstType;
+  AssertEquals('Name',Unicodestring('a'),T.Name);
+  A:=TJSArrayTypeDef(CheckClass(T.TypeDef,TJSArrayTypeDef,'Type def class'));
+  U:=TJSUnionTypeDef(CheckClass(A.BaseType,TJSUnionTypeDef,'base type Union class '));
+  AssertEquals('Union Count',2,U.TypeCount);
+  R:=TJSTypeReference(CheckClass(U.Types[0],TJSTypeReference,'Union class element 0'));
+  AssertEquals('Name base type',Unicodestring('string'),R.Name);
+  R:=TJSTypeReference(CheckClass(U.Types[1],TJSTypeReference,'Union class element 1'));
+  AssertEquals('Name base type',Unicodestring('number'),R.Name);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareTypeArrayOfFunctionType;
+Var
+  T : TJSTypeDeclaration;
+  A : TJSArrayTypeDef;
+  F : TJSArrowFunctionTypeDef;
+  R : TJSTypeReference;
+
+begin
+  StartTS('type a = (() => string)[]');
+  T:=GetFirstType;
+  AssertEquals('Name',Unicodestring('a'),T.Name);
+  A:=TJSArrayTypeDef(CheckClass(T.TypeDef,TJSArrayTypeDef,'Type def class'));
+  F:=TJSArrowFunctionTypeDef(CheckClass(A.BaseType,TJSArrowFunctionTypeDef,'Have arrow function'));
+  AssertNotNull('Have function definition',F.aFunction);
+  R:=TJSTypeReference(CheckClass(F.aFunction.ResultType,TJSTypeReference,'Have function result'));
+  AssertEquals('result type name',Unicodestring('string'),R.Name);
+  AssertEquals('function argument count',0,F.aFunction.TypedParams.Count);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareTypeArrayGeneric;
+
+Var
+  T : TJSTypeDeclaration;
+  A : TJSArrayTypeDef;
+  R : TJSTypeReference;
+
+begin
+  StartTS('type a = Array<string>');
+  T:=GetFirstType;
+  AssertEquals('Name',Unicodestring('a'),T.Name);
+  AssertNotNull('Have type def',T.TypeDef);
+  AssertEquals('Correct type def class',TJSArrayTypeDef,T.TypeDef.ClassType);
+  A:=TJSArrayTypeDef(T.TypeDef);
+  AssertNotNull('have base type',A.BaseType);
+  AssertEquals('Correct type of base type',TJSTypeReference, A.BaseType.ClassType);
+  R:=A.BaseType as TJSTypeReference;
+  AssertEquals('Name base type',Unicodestring('string'),R.Name);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareTypeGeneric;
+Var
+  T : TJSTypeDeclaration;
+  G : TJSGenericTypeRef;
+  R : TJSTypeReference;
+
+begin
+  StartTS('type a = Shell<string>');
+  T:=GetFirstType;
+  AssertEquals('Name',Unicodestring('a'),T.Name);
+  AssertNotNull('Have type def',T.TypeDef);
+  AssertEquals('Correct type def class',TJSGenericTypeRef,T.TypeDef.ClassType);
+  G:=TJSGenericTypeRef(T.TypeDef);
+  AssertNotNull('have base type',G.BaseType);
+  AssertEquals('Correct type of base type',TJSTypeReference, G.BaseType.ClassType);
+  R:=G.BaseType as TJSTypeReference;
+  AssertEquals('Name base type',Unicodestring('Shell'),R.Name);
+  AssertEquals('Correct number of parameters',1, G.ParamTypeCount);
+  AssertEquals('Correct type of parameter 0',TJSTypeReference, G.ParamTypes[0].Classtype);
+  R:=G.ParamTypes[0] as TJSTypeReference;
+  AssertEquals('Name base type',Unicodestring('string'),R.Name);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareTypeGenericGeneric;
+Var
+  T : TJSTypeDeclaration;
+  G : TJSTupleTypeDef;
+  R : TJSTypeReference;
+
+begin
+  StartTS('type Pair<T> = [T,T]');
+  T:=GetFirstType;
+  AssertEquals('Name',Unicodestring('Pair'),T.Name);
+  AssertNotNull('Have type def',T.TypeDef);
+  AssertEquals('Correct number of parameters',1, T.TypeParams.Count);
+  AssertEquals('Correct type of parameter 0',TJSTypeReference, T.TypeParams[0].Node.Classtype);
+  R:=T.TypeParams[0].Node as TJSTypeReference;
+  AssertEquals('Name base type',Unicodestring('T'),R.Name);
+  AssertEquals('Correct type def class',TJSTupleTypeDef,T.TypeDef.ClassType);
+  G:=TJSTupleTypeDef(T.TypeDef);
+  AssertEquals('Correct number of parameters',2, G.TypeCount);
+  AssertEquals('Correct type of parameter 0',TJSTypeReference, G.Types[0].Classtype);
+  R:=G.Types[0] as TJSTypeReference;
+  AssertEquals('Name base type',Unicodestring('T'),R.Name);
+  AssertEquals('Correct type of parameter 1',TJSTypeReference, G.Types[1].Classtype);
+  R:=G.Types[1] as TJSTypeReference;
+  AssertEquals('Name base type',Unicodestring('T'),R.Name);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareTypeObject;
+
+Var
+  T : TJSTypeDeclaration;
+  O : TJSObjectTypeDef;
+  R : TJSTypeReference;
+
+begin
+  StartTS('type TreeItem = {left : TreeItem, right : TreeItem};');
+  T:=GetFirstType;
+  AssertEquals('Name',Unicodestring('TreeItem'),T.Name);
+  AssertNotNull('Have type def',T.TypeDef);
+  AssertEquals('Correct type of typedef',TJSObjectTypeDef, T.TypeDef.ClassType);
+  O:=TJSObjectTypeDef(T.TypeDef);
+  AssertEquals('Correct number of elements',2, O.ElementCount);
+  AssertEquals('Correct name of element 0',UnicodeString('left'), O.Elements[0].Name);
+  AssertEquals('Correct type of element 0',TJSTypeReference, O.Elements[0].ElementType.ClassType);
+  R:=O.Elements[0].ElementType as TJSTypeReference;
+  AssertEquals('Name base type',Unicodestring('TreeItem'),R.Name);
+  AssertEquals('Correct name of element 0',UnicodeString('right'), O.Elements[1].Name);
+  AssertEquals('Correct type of element 1',TJSTypeReference, O.Elements[1].ElementType.ClassType);
+  R:=O.Elements[1].ElementType as TJSTypeReference;
+  AssertEquals('Name base type',Unicodestring('TreeItem'),R.Name);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareTypeArrowFunction;
+
+Var
+  T : TJSTypeDeclaration;
+  A : TJSArrowFunctionTypeDef;
+  P : TJSTypedParam;
+  R : TJSTypeReference;
+
+begin
+  StartTS('type CallBack = (data : t) => void;');
+  T:=GetFirstType;
+  AssertEquals('Name',Unicodestring('CallBack'),T.Name);
+  A:=TJSArrowFunctionTypeDef(CheckClass(T.TypeDef,TJSArrowFunctionTypeDef,'Have arrow function'));
+  AssertNotNull('Have function definition',A.aFunction);
+  R:=TJSTypeReference(CheckClass(A.aFunction.ResultType,TJSTypeReference,'Have function result'));
+  AssertEquals('result type name',Unicodestring('void'),R.Name);
+  AssertEquals('function argument count',1,A.aFunction.TypedParams.Count);
+  P:=A.aFunction.TypedParams.Params[0];
+  AssertEquals('param name',Unicodestring('data'),P.Name);
+  R:=TJSTypeReference(CheckClass(P.Node,TJSTypeReference,'Have param type'));
+  AssertEquals('param type name',Unicodestring('t'),R.Name);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareTypeArrowFunctionShort;
+Var
+  T : TJSTypeDeclaration;
+  A : TJSArrowFunctionTypeDef;
+  R : TJSTypeReference;
+
+begin
+  StartTS('type CallBack = () => void;');
+  T:=GetFirstType;
+  AssertEquals('Name',Unicodestring('CallBack'),T.Name);
+  A:=TJSArrowFunctionTypeDef(CheckClass(T.TypeDef,TJSArrowFunctionTypeDef,'Have arrow function'));
+  AssertNotNull('Have function definition',A.aFunction);
+  R:=TJSTypeReference(CheckClass(A.aFunction.ResultType,TJSTypeReference,'Have function result'));
+  AssertEquals('result type name',Unicodestring('void'),R.Name);
+  AssertEquals('function argument count',0,A.aFunction.TypedParams.Count);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareTypeArrowFunctionTwoArgs;
+Var
+  T : TJSTypeDeclaration;
+  A : TJSArrowFunctionTypeDef;
+  P : TJSTypedParam;
+  R : TJSTypeReference;
+
+begin
+  StartTS('type CallBack = (data : t, context: t2) => void;');
+  T:=GetFirstType;
+  AssertEquals('Name',Unicodestring('CallBack'),T.Name);
+  A:=TJSArrowFunctionTypeDef(CheckClass(T.TypeDef,TJSArrowFunctionTypeDef,'Have arrow function'));
+  AssertNotNull('Have function definition',A.aFunction);
+  R:=TJSTypeReference(CheckClass(A.aFunction.ResultType,TJSTypeReference,'Have function result'));
+  AssertEquals('result type name',Unicodestring('void'),R.Name);
+  AssertEquals('function argument count',2,A.aFunction.TypedParams.Count);
+  P:=A.aFunction.TypedParams.Params[0];
+  AssertEquals('param name',Unicodestring('data'),P.Name);
+  R:=TJSTypeReference(CheckClass(P.Node,TJSTypeReference,'Have param 0 type'));
+  AssertEquals('param type name',Unicodestring('t'),R.Name);
+  P:=A.aFunction.TypedParams.Params[1];
+  AssertEquals('param name',Unicodestring('context'),P.Name);
+  R:=TJSTypeReference(CheckClass(P.Node,TJSTypeReference,'Have param 1 type'));
+  AssertEquals('param type name',Unicodestring('t2'),R.Name);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareEnum;
+
+Var
+  E : TJSEnumDeclaration;
+  ET : TJSEnumTypeDef;
+
+begin
+  StartTS('enum Colors {red,blue,green};');
+  E:=GetFirstEnum;
+  AssertEquals('Name',Unicodestring('Colors'),E.Name);
+  ET:=TJSEnumTypeDef(CheckClass(E.EnumDef,TJSEnumTypeDef,'Have enum definition'));
+  AssertEquals('Member count',3,ET.NameCount);
+  AssertEquals('Member 0',UnicodeString('red'),ET.Names[0]);
+  AssertEquals('Member 1',UnicodeString('blue'),ET.Names[1]);
+  AssertEquals('Member 2',UnicodeString('green'),ET.Names[2]);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareInterfaceEmpty;
+
+Var
+  Decl : TJSInterfaceDeclaration;
+
+begin
+  StartTS('interface Empty {};');
+  Decl:=TJSInterfaceDeclaration(CheckClass(GetFirstInterface,TJSInterfaceDeclaration,'Interface'));
+  AssertNotNull('Have declaration');
+  AssertEquals('Name',Unicodestring('Empty'),Decl.Name);
+  AssertNull('No Extends',Decl.Extends);
+//  AssertEquals('Extends count',0,Decl.Extends.Count);
+  AssertEquals('Members count',0,Decl.Values.Count);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareAmbientInterfaceEmpty;
+
+Var
+  Decl : TJSInterfaceDeclaration;
+
+begin
+  StartTS('declare interface Empty {};');
+  Decl:=TJSInterfaceDeclaration(CheckClass(GetFirstInterface,TJSInterfaceDeclaration,'Interface'));
+  AssertEquals('First type node is type declaration',True,GetTypes.Nodes[0].IsAmbient);
+  AssertNotNull('Have declaration');
+  AssertEquals('Name',Unicodestring('Empty'),Decl.Name);
+  AssertNull('No Extends',Decl.Extends);
+//  AssertEquals('Extends count',0,Decl.Extends.Count);
+  AssertEquals('Members count',0,Decl.Values.Count);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareInterfaceExtendsEmpty;
+Var
+  Decl : TJSInterfaceDeclaration;
+  Lit : TJSLiteral;
+
+begin
+  StartTS('interface Empty extends ParentEmpty {};');
+  Decl:=TJSInterfaceDeclaration(CheckClass(GetFirstInterface,TJSInterfaceDeclaration,'Interface'));
+  AssertNotNull('Have declaration');
+  AssertEquals('Name',Unicodestring('Empty'),Decl.Name);
+  AssertNotNull('Extends',Decl.Extends);
+  AssertEquals('Extends count',1,Decl.Extends.Count);
+  Lit:=TJSLiteral(CheckClass(Decl.Extends.Nodes[0].Node,TJSLiteral,'Literal 0 '));
+  AssertNotNull('Lit has value',Lit.Value);
+  AssertEquals('Extends name',UnicodeString('ParentEmpty'),Lit.Value.AsString);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareInterfaceExtendsTwoEmpty;
+Var
+  Decl : TJSInterfaceDeclaration;
+  Lit : TJSLiteral;
+
+begin
+  StartTS('interface Empty extends ParentEmpty,MoreEmpty {};');
+  Decl:=TJSInterfaceDeclaration(CheckClass(GetFirstInterface,TJSInterfaceDeclaration,'Interface'));
+  AssertNotNull('Have declaration');
+  AssertEquals('Name',Unicodestring('Empty'),Decl.Name);
+  AssertNotNull('Extends',Decl.Extends);
+  AssertEquals('Extends count',2,Decl.Extends.Count);
+  Lit:=TJSLiteral(CheckClass(Decl.Extends.Nodes[0].Node,TJSLiteral,'Literal 0 '));
+  AssertNotNull('Lit has value',Lit.Value);
+  AssertEquals('Extends name',UnicodeString('ParentEmpty'),Lit.Value.AsString);
+  Lit:=TJSLiteral(CheckClass(Decl.Extends.Nodes[1].Node,TJSLiteral,'Literal 0 '));
+  AssertNotNull('Lit has value',Lit.Value);
+  AssertEquals('Extends name',UnicodeString('MoreEmpty'),Lit.Value.AsString);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareInterfaceProperty;
+
+Var
+  Decl : TJSInterfaceDeclaration;
+  Prop : TJSPropertyDeclaration;
+  R : TJSTypeReference;
+
+begin
+  StartTS('interface Friend { name: string; };');
+  Decl:=TJSInterfaceDeclaration(CheckClass(GetFirstInterface,TJSInterfaceDeclaration,'Interface'));
+  AssertNotNull('Have declaration');
+  AssertEquals('Name',Unicodestring('Friend'),Decl.Name);
+  AssertNull('Extends',Decl.Extends);
+  AssertEquals('Member count',1,Decl.ElementCount);
+  Prop:=TJSPropertyDeclaration(CheckClass('Property class',TJSPropertyDeclaration,Decl.Elements[0]));
+  AssertEquals('Property name',UnicodeString('name'),Prop.Name);
+  AssertEquals('Property optional',False,Prop.Optional);
+  R:=TJSTypeReference(CheckClass('Property type',TJSTypeReference,Prop.ElementType));
+  AssertEquals('Property type name',UnicodeString('string'),R.Name);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareInterfaceOptionalProperty;
+Var
+  Decl : TJSInterfaceDeclaration;
+  Prop : TJSPropertyDeclaration;
+  R : TJSTypeReference;
+
+begin
+  StartTS('interface Friend { name ? : string; };');
+  Decl:=TJSInterfaceDeclaration(CheckClass(GetFirstInterface,TJSInterfaceDeclaration,'Interface'));
+  AssertNotNull('Have declaration');
+  AssertEquals('Name',Unicodestring('Friend'),Decl.Name);
+  AssertNull('Extends',Decl.Extends);
+  AssertEquals('Member count',1,Decl.ElementCount);
+  Prop:=TJSPropertyDeclaration(CheckClass('Property class',TJSPropertyDeclaration,Decl.Elements[0]));
+  AssertEquals('Property name',UnicodeString('name'),Prop.Name);
+  AssertEquals('Property optional',True,Prop.Optional);
+  R:=TJSTypeReference(CheckClass('Property type',TJSTypeReference,Prop.ElementType));
+  AssertEquals('Property type name',UnicodeString('string'),R.Name);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareInterfaceUntypedProperty;
+
+Var
+  Decl : TJSInterfaceDeclaration;
+  Prop : TJSPropertyDeclaration;
+  R : TJSTypeReference;
+
+begin
+  StartTS('interface Friend { name ; };');
+  Decl:=TJSInterfaceDeclaration(CheckClass(GetFirstInterface,TJSInterfaceDeclaration,'Interface'));
+  AssertNotNull('Have declaration');
+  AssertEquals('Name',Unicodestring('Friend'),Decl.Name);
+  AssertNull('Extends',Decl.Extends);
+  AssertEquals('Member count',1,Decl.ElementCount);
+  Prop:=TJSPropertyDeclaration(CheckClass('Property class',TJSPropertyDeclaration,Decl.Elements[0]));
+  AssertEquals('Property name',UnicodeString('name'),Prop.Name);
+  AssertEquals('Property optional',False,Prop.Optional);
+  R:=TJSTypeReference(CheckClass('Property type',TJSTypeReference,Prop.ElementType));
+  AssertEquals('Property type name',UnicodeString('any'),R.Name);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareInterfaceOptionalUntypedProperty;
+
+Var
+  Decl : TJSInterfaceDeclaration;
+  Prop : TJSPropertyDeclaration;
+  R : TJSTypeReference;
+
+begin
+  StartTS('interface Friend { name ? ; };');
+  Decl:=TJSInterfaceDeclaration(CheckClass(GetFirstInterface,TJSInterfaceDeclaration,'Interface'));
+  AssertNotNull('Have declaration');
+  AssertEquals('Name',Unicodestring('Friend'),Decl.Name);
+  AssertNull('Extends',Decl.Extends);
+  AssertEquals('Member count',1,Decl.ElementCount);
+  Prop:=TJSPropertyDeclaration(CheckClass('Property class',TJSPropertyDeclaration,Decl.Elements[0]));
+  AssertEquals('Property name',UnicodeString('name'),Prop.Name);
+  AssertEquals('Property optional',True,Prop.Optional);
+  R:=TJSTypeReference(CheckClass('Property type',TJSTypeReference,Prop.ElementType));
+  AssertEquals('Property type name',UnicodeString('any'),R.Name);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareInterfaceMethod;
+
+Var
+  Decl : TJSInterfaceDeclaration;
+  M : TJSMethodDeclaration;
+  R : TJSTypeReference;
+
+begin
+  StartTS('interface JQuery { text (content: string) : string; };');
+  Decl:=TJSInterfaceDeclaration(CheckClass(GetFirstInterface,TJSInterfaceDeclaration,'Interface'));
+  AssertNotNull('Have declaration');
+  AssertEquals('Name',Unicodestring('JQuery'),Decl.Name);
+  AssertNull('Extends',Decl.Extends);
+  AssertEquals('Member count',1,Decl.ElementCount);
+  M:=TJSMethodDeclaration(CheckClass('Method class',TJSMethodDeclaration,Decl.Elements[0]));
+  AssertEquals('Method name',UnicodeString('text'),M.Name);
+  AssertEquals('Method optional',False,M.Optional);
+  AssertNotNull('Have function def',M.FuncDef);
+  R:=TJSTypeReference(CheckClass('Result type',TJSTypeReference,M.FuncDef.ResultType));
+  AssertEquals('Property type name',UnicodeString('string'),R.Name);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareInterfaceMethodNoReturn;
+Var
+  Decl : TJSInterfaceDeclaration;
+  M : TJSMethodDeclaration;
+  R : TJSTypeReference;
+
+begin
+  StartTS('interface JQuery { text (content: string) ; };');
+  Decl:=TJSInterfaceDeclaration(CheckClass(GetFirstInterface,TJSInterfaceDeclaration,'Interface'));
+  AssertNotNull('Have declaration');
+  AssertEquals('Name',Unicodestring('JQuery'),Decl.Name);
+  AssertNull('Extends',Decl.Extends);
+  AssertEquals('Member count',1,Decl.ElementCount);
+  M:=TJSMethodDeclaration(CheckClass('Method class',TJSMethodDeclaration,Decl.Elements[0]));
+  AssertEquals('Method name',UnicodeString('text'),M.Name);
+  AssertEquals('Method optional',False,M.Optional);
+  AssertNotNull('Have function def',M.FuncDef);
+  R:=TJSTypeReference(CheckClass('Result type',TJSTypeReference,M.FuncDef.ResultType));
+  AssertEquals('Property type name',UnicodeString('any'),R.Name);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareInterfaceCallable;
+Var
+  Decl : TJSInterfaceDeclaration;
+  M : TJSMethodDeclaration;
+  R : TJSTypeReference;
+
+begin
+  StartTS('interface JQuery { (query : string) : JQuery; };');
+  Decl:=TJSInterfaceDeclaration(CheckClass(GetFirstInterface,TJSInterfaceDeclaration,'Interface'));
+  AssertNotNull('Have declaration');
+  AssertEquals('Name',Unicodestring('JQuery'),Decl.Name);
+  AssertNull('Extends',Decl.Extends);
+  AssertEquals('Member count',1,Decl.ElementCount);
+  M:=TJSMethodDeclaration(CheckClass('Method class',TJSMethodDeclaration,Decl.Elements[0]));
+  AssertEquals('Method name',UnicodeString(''),M.Name);
+  AssertEquals('Method optional',False,M.Optional);
+  AssertNotNull('Have function def',M.FuncDef);
+  R:=TJSTypeReference(CheckClass('Result type',TJSTypeReference,M.FuncDef.ResultType));
+  AssertEquals('Property type name',UnicodeString('JQuery'),R.Name);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareInterfaceMethodThisReturn;
+Var
+  Decl : TJSInterfaceDeclaration;
+  M : TJSMethodDeclaration;
+  R : TJSTypeReference;
+
+begin
+  StartTS('interface JQuery { text (content: string) : this; };');
+  Decl:=TJSInterfaceDeclaration(CheckClass(GetFirstInterface,TJSInterfaceDeclaration,'Interface'));
+  AssertNotNull('Have declaration');
+  AssertEquals('Name',Unicodestring('JQuery'),Decl.Name);
+  AssertNull('Extends',Decl.Extends);
+  AssertEquals('Member count',1,Decl.ElementCount);
+  M:=TJSMethodDeclaration(CheckClass('Method class',TJSMethodDeclaration,Decl.Elements[0]));
+  AssertEquals('Method name',UnicodeString('text'),M.Name);
+  AssertEquals('Method optional',False,M.Optional);
+  AssertNotNull('Have function def',M.FuncDef);
+  R:=TJSTypeReference(CheckClass('Result type',TJSTypeReference,M.FuncDef.ResultType));
+  AssertEquals('Property type name',UnicodeString('this'),R.Name);
+end;
+
+procedure TTestTypeScriptParser.TestDeclareClassEmpty;
+Var
+  Decl : TJSClassDeclaration;
+  M : TJSMethodDeclaration;
+  R : TJSTypeReference;
+
+begin
+  StartTS('declare class JQuery {};');
+  Decl:=TJSClassDeclaration(CheckClass('Class',TJSClassDeclaration,GetFirstClass));
+  AssertNotNull('Have declaration');
+  AssertEquals('Name',Unicodestring('JQuery'),Decl.Name);
+  AssertEquals('Extends','',Decl.Extends);
+  CheckMembers(Decl.Members,0,0,0,0,0,0,0);
+end;
+
+initialization
+  RegisterTest(TTestTypeScriptParser);
+end.
+

+ 166 - 0
packages/fcl-js/tests/tctstopas.pp

@@ -0,0 +1,166 @@
+unit tctstopas;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+  Classes, SysUtils, fpcunit, testregistry, tstopas;
+
+Type
+
+  { TTestTSToPas }
+  TMyTypescriptToPas = class(TTypescriptToPas)
+  end;
+
+  TTestTSToPas = Class(TTestCase)
+  private
+    FConverter: TTypescriptToPas;
+  Public
+    Procedure Setup; override;
+    procedure TearDown; override;
+    procedure Convert(aSource : string); overload;
+    procedure Convert(aSource : TStrings); overload;
+    procedure CheckDeclaration(const aSection, aDeclaration : String);
+    procedure CheckDeclaration(const aSection, aDeclaration, aDeclaration2 : String);
+    procedure CheckDeclarations(const aSection : String; Const Declarations : Array of string);
+    Property Converter : TTypescriptToPas Read FConverter;
+  Published
+    Procedure TestEmpty;
+    Procedure TestVarDeclaration;
+    Procedure Test2VarDeclarations;
+    Procedure Test3VarDeclarations;
+    Procedure TestKeywordVarDeclaration;
+    Procedure TestSimpleType;
+  end;
+
+implementation
+
+{ TTestTSToPas }
+
+procedure TTestTSToPas.Setup;
+
+begin
+  inherited Setup;
+  FConverter:=TMyTypescriptToPas.Create(Nil);
+  FConverter.Options:=FConverter.Options+[coRaw];
+end;
+
+procedure TTestTSToPas.TearDown;
+
+begin
+  FreeAndNil(FConverter);
+  inherited TearDown;
+end;
+
+procedure TTestTSToPas.Convert(aSource: string);
+
+Var
+  aSrc : TStrings;
+
+begin
+  aSrc:=TStringList.Create;
+  try
+    TStringList(aSrc).SkipLastLineBreak:=True;
+    aSrc.Text:=aSource;
+    Writeln('--');
+    Writeln(aSrc.Text);
+    Writeln('--');
+    Convert(aSrc);
+  finally
+    aSrc.Free;
+  end;
+
+end;
+
+procedure TTestTSToPas.Convert(aSource: TStrings);
+Var
+  S : TStream;
+begin
+  S:=TStringStream.Create(aSource.text);
+  try
+    FConverter.InputStream:=S;
+    FConverter.Execute;
+  finally
+    S.Free;
+  end;
+end;
+
+procedure TTestTSToPas.CheckDeclaration(const aSection, aDeclaration: String);
+
+begin
+  CheckDeclarations(aSection,[aDeclaration]);
+end;
+
+procedure TTestTSToPas.CheckDeclaration(const aSection, aDeclaration, aDeclaration2: String);
+
+begin
+  CheckDeclarations(aSection,[aDeclaration,aDeclaration2]);
+end;
+
+procedure TTestTSToPas.CheckDeclarations(const aSection: String; const Declarations: array of string);
+Var
+  Src : TStrings;
+  I,J : Integer;
+  D,S,actSrc : String;
+
+begin
+  Src:=FConverter.Source;
+  Writeln(Src.Text);
+  I:=0;
+  While (I<Src.Count) and (Trim(Src[i])='') do
+    Inc(I);
+  AssertTrue('Section: Not at end',I<Src.Count);
+  AssertEquals('Section correct',LowerCase(aSection),LowerCase(Trim(Src[i])));
+  For J:=0 to Length(Declarations)-1 do
+    begin
+    D:=Format('Declaration %d: ',[J]);
+    S:=Declarations[J];
+    Inc(I);
+    While (I<Src.Count) and (Trim(Src[i])='') do
+      Inc(I);
+    AssertTrue(D+'Not at end',I<Src.Count);
+    actSrc:=Src[i];
+    AssertEquals(D+'Declaration correct',LowerCase(S),LowerCase(Trim(actSrc)));
+    end;
+end;
+
+procedure TTestTSToPas.TestEmpty;
+begin
+  AssertNotNull(Converter);
+end;
+
+procedure TTestTSToPas.TestVarDeclaration;
+begin
+  Convert('declare var x : number;');
+  CheckDeclaration('var','x : double;');
+end;
+
+procedure TTestTSToPas.Test2VarDeclarations;
+begin
+  Convert('declare var x,y : number;');
+  CheckDeclaration('var','x : double;','y : double;');
+end;
+
+procedure TTestTSToPas.Test3VarDeclarations;
+begin
+  Convert('declare var x,y,z : number;');
+  CheckDeclarations('var',['x : double;','y : double;','z : double;']);
+end;
+
+procedure TTestTSToPas.TestKeywordVarDeclaration;
+begin
+  Convert('declare var on : string;');
+  CheckDeclarations('var',['&on : string; external name ''on'';']);
+end;
+
+procedure TTestTSToPas.TestSimpleType;
+begin
+  Convert('declare type MyType = string;');
+  CheckDeclarations('type',['TMyType = string;']);
+end;
+
+Initialization
+  Registertest(TTestTSToPas);
+end.
+

+ 25 - 4
packages/fcl-js/tests/testjs.lpi

@@ -1,10 +1,15 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <CONFIG>
   <ProjectOptions>
-    <Version Value="10"/>
+    <Version Value="12"/>
     <General>
+      <Flags>
+        <MainUnitHasCreateFormStatements Value="False"/>
+        <MainUnitHasTitleStatement Value="False"/>
+        <MainUnitHasScaledStatement Value="False"/>
+        <CompatibilityMode Value="True"/>
+      </Flags>
       <SessionStorage Value="InProjectDir"/>
-      <MainUnit Value="0"/>
       <UseAppBundle Value="False"/>
     </General>
     <BuildModes Count="1">
@@ -15,15 +20,23 @@
     </PublishOptions>
     <RunParams>
       <local>
-        <FormatVersion Value="1"/>
+        <CommandLineParams Value="--suite=TTestTSToPas.TestSimpleType"/>
       </local>
+      <FormatVersion Value="2"/>
+      <Modes Count="1">
+        <Mode0 Name="default">
+          <local>
+            <CommandLineParams Value="--suite=TTestTSToPas.TestSimpleType"/>
+          </local>
+        </Mode0>
+      </Modes>
     </RunParams>
     <RequiredPackages Count="1">
       <Item1>
         <PackageName Value="FCL"/>
       </Item1>
     </RequiredPackages>
-    <Units Count="13">
+    <Units Count="15">
       <Unit0>
         <Filename Value="testjs.lpr"/>
         <IsPartOfProject Value="True"/>
@@ -79,6 +92,14 @@
         <IsPartOfProject Value="True"/>
         <UnitName Value="JSSrcMap"/>
       </Unit12>
+      <Unit13>
+        <Filename Value="tctsparser.pp"/>
+        <IsPartOfProject Value="True"/>
+      </Unit13>
+      <Unit14>
+        <Filename Value="tctstopas.pp"/>
+        <IsPartOfProject Value="True"/>
+      </Unit14>
     </Units>
   </ProjectOptions>
   <CompilerOptions>

+ 3 - 2
packages/fcl-js/tests/testjs.lpr

@@ -6,8 +6,9 @@ uses
   {$IFDEF Unix}
   cwstring,
   {$ENDIF}
-  Classes, consoletestrunner, tcscanner, jsparser, jsscanner, jstree, jsbase,
-  tcparser, jswriter, tcwriter, jstoken, JSSrcMap, TCSrcMap;
+  Classes, consoletestrunner,
+  // tests
+  tcscanner, tcparser, tcwriter, TCSrcMap, tctsparser, tctstopas;
 
 var
   Application: TTestRunner;

Some files were not shown because too many files changed in this diff