can I use an interface for a property list? - interface

I have an interface def
In order to use in many places, i understand I have to split it into its own file (at least with ES5 output). So:
//ICommand.ts>
interface RegExp {
$1: string;
$2: string;
$3: string;
// etc
}
interface IBotCommand {
regex: RegExp
// fixme - clearer def
// cmd: ():Output
cmd: any
}
export = IBotCommand;
//BotCommand.ts >
import ICommand = require("./ICommand");
class BotCommand {
commandList: ICommand
But this gives Error: cannot find name ICommand
Is this possible? What is the best syntax?

it seems this maybe down to my environment not recompiling these scripts.
also the RegExp definition is taken from here:
TypeScript and RegExp
and may not be fully functional

You are exporting the interface like this:
export = IBotCommand;
// but you are importing it with:
import ICommand = require('./ICommand');
With you export being the way it is, change your import to be:
import { IBotCommand } from './ICommand';
If you'd like to import it like this:
import ICommand from './ICommand';
then you will want to export it like this:
export default IBotCommand;
If you don't use default then you must use the object notation to reference your imports.
There is a caveat to the object notation, in that you could also do this:
import * as cmds from './ICommand';
// and use it like this:
class SomeClass implements cmds.IBotCommand {}

Related

How to build an abstract with a Haxe macro?

I couldn't find any example code or tutorial floating around that creates abstracts with macros.
//Class code ReflectionClassInfo.hx
#:build(ReflectionClassInfoMacro.build())
abstract ReflectionClassInfo({}) from ({}) to ({}) {}
//Driver code
var r=new ReflectionClassInfo();
//Immeditately makes the compiler complain about there is no constructor
How can I fix the compiler error?
One thing that's important to realize is that there isn't really any difference between build macros for classes and abstracts. In both cases, they build fields, meaning that they have to return an array of haxe.macro.Expr.Field. So any documentation or code example that applies to one also applies to the other.
The easiest / most readable way to fix the compiler error in your example is by using class reification, so that the constructor can be declared with regular Haxe syntax:
import haxe.macro.Context;
import haxe.macro.Expr.Field;
class Macro {
public static function build():Array<Field> {
var fields = Context.getBuildFields();
fields = fields.concat((macro class {
public function new() {
this = {};
}
}).fields);
return fields;
}
}
class Main {
static function main() {
new Abstract(); // compiles
}
}
#:build(Macro.build())
abstract Abstract({}) from {} to {} {}

In Haxe (JS Target) is there a way to make static members available to another class as if it was its own?

I have this Haxe class that is growing quite large. It consists mostly of static methods & properties. (It's a module set to compile as JS target).
I would like to separate some of the complex static functions into another class.
Is there any way to mark it with a metatag / indicate the other class is an "extension" to the original class?
Something like #:native("OriginalClass") class OtherClass {...}
The goal is to avoid having to write the full variable access (ex: OriginalClass.LOG_QUEUE vs. LOG_QUEUE) or clutter the imports with each OriginalClass's static methods / properties used at the top of the OtherClass. Basically, something to make it aware that it "is" using the same members as the OriginalClass (whenever an 'undefined' one is found, at compile-time).
Example:
If OriginalClass has static var LOG_QUEUE:Array<String>; then OtherClass would be aware that any usage of LOG_QUEUE compiles to this JS code OriginalClass.LOG_QUEUE
Alright, got a solution after discussing with Dima Granetchi from the Haxe experts group on Slack.
Now, although this will still generate the OtherClass that makes use of the OriginalClass's static members, you can cut down on the quantity of import statements for most (if not all) of the module/class's static members by using the wildcard * symbol, like in this example:
// OriginalClass.hx
package somePackage;
class OriginalClass {
public static var LOG_QUEUE:Array<String>;
public static function main() {
LOG_QUEUE = [];
OtherClass.doSomething();
}
public static function doSomethingOriginal() {
LOG_QUEUE.push("World!");
}
}
// OtherClass.hx
import somePackage.OriginalClass.*; // <-- Demonstrating the WILDCARD (*) symbol
class OtherClass {
public static function doSomething() {
LOG_QUEUE.push("Hello"); //Resolved to OriginalClass.LOG_QUEUE
doSomethingOriginal(); //Resolved to OriginalClass.doSomethingOriginal()
}
}
Although this is a minimal example, it becomes more useful when you have a few different dozen static members used in your OtherClass.
Note
TypeDefs defined in the OriginalClass used inside the OtherClass doesn't seem to get recognized/resolved (may be due to missing public accessor, but I was unable to set it on my typedefs). You can always import those specific TypeDefs with individual import statements, like so:
//Somewhere at the top of OtherClass.hx...
import somePackage.OriginalClass.MyTypeDef;

How can I export an interface that I have imported?

I am creating a library in typescript, which is spread across multiple files. I take all the classes and constants I have defines and import them into one module, which exports them all under one namespace. I have just defines an interface, and I wish to include it in the same namespace/module as all the other parts of my library. But apparently I can't.
Here's a simplified example:
/app.ts is the entry point of the application, all I do in it at the moment is include my library MyLib:
//app.ts
import myLib = require("lib/MyLib/MyLib"); // this works fine
/lib/MyLib/MyLib.ts is the file in which I import all of the things defined by MyLib, and export them together:
// lib/MyLib/MyLib.ts
import VehiclesImport = require("./transport/vehicles");
// error under VehiclesImport.IAutomobile, saying that VehiclesImport has no property IAutomobile
export var IAutomobile = VehiclesImport.IAutomobile;
export var Car = VehiclesImport.Car;
In /lib/MyLib/transport/vehicles.ts, I define several classes and interfaces of vehicles, here, I'll just show IAutomobile and Car:
// lib/MyLib/transport/vehicles.ts
export interface IAutomobile {
weight: number
}
export class Car implements IAutomobile {
weight = 3000
}
I have tried creating a class truck in MyLib.ts, which properly implements IAutomobile, and that works fine, without any error messages. The problem only seems to arise when I want to access IAutomobile outside of an 'implements' statement.
I apologize if this seems like a 'code dump', but in my opinion, this is a serious problem that I cannot access my interfaces except in a class declaration. I have searched Google for the past two hours and found nothing on the subject. Thanks for any help you can give me!
Edit: I understand that typescript interfaces are not part of the compiled javascript code, but that should not stop me from manipulating them within typescript.
Use the import keyword to bring in something into the type declaration space (as opposed to var which brings it into the variable declaration space).
This is demonstrated below. a.ts:
export interface A {
val: number;
}
To re-export this from another file b.ts:
import a = require('./a');
export import B = a.A; // Important use of import
Sample usage in some other file c.ts:
import b = require('./b');
var foo: b.B;
foo.val = 123;
interface C extends b.B {
val2:number;
}
var bar: C;
bar.val2 = 456;
The example rewritten following TS language specification:
a.ts:
export interface A {
val: number;
}
To re-export this from another file b.ts:
export {A} from './a'
Usage in some other file c.ts:
import {A} from './b'
var foo: A = {val: 2};
foo.val = 123;
interface C extends A {
val2:number;
}
var bar: C = {val: 1, val2: 3};
bar.val2 = 456;
Types can't be assigned to variables, they exist in different "declaration spaces". Classes can be assigned to variables, because they contribute their names to the type declaration space as well as defining the class objects. Interfaces only contribute to the types declaration space, so can't be referenced as values.
The language is a bit verbose, but this is spelt out in detail in section 2.3 of the language spec
foo.ts
export interface ITest {
...
}
bar.ts
import * as foo from "./foo";
export type ITest = foo.ITest;
This works to re-export types/interfaces
import type { MyInterface, MyType } from './randomModule';
export { MyInterface, MyType }
The key is the surrounding braces in the export statement. Works in TypeScript 4.7.4. Reference.
In TypeScript 3.9.6, this worked for me:
import { Foo as FooType } from './some-path';
export type Foo = FooType;

Extending a class in another file

I have some TypeScript code that is being generated by a tool. I'd like to extend this class in another file. As of 0.9.1.1, what's the best way to go about this?
I thought maybe I could staple my additional functions onto the prototype, but this is giving various errors (which change depending what mood the compiler is in).
For example:
Foo.ts (generated by a tool)
module MyModule {
export class Dog { }
}
Bar.ts
module MyModule {
function bark(): string {return 'woof';}
Dog.prototype.bark = bark;
}
You cannot split a class definition between multiple files in TypeScript. However typescript understands how JavaScript works and will let you write idomatic JavaScript classes just fine:
module MyModule {
export function Dog(){};
}
module MyModule {
function bark(): string {return 'woof';}
Dog.prototype.bark = bark;
}
Try it online
One way around this is to use inheritance:
class BigDog extends Dog{
bark(){}
}
I have encountered your problem as well before, but I had some deeper problems. You can see from basarat's example, that simple functions can be added as an extension to the prototype, but when it comes to static functions, or other static values you might want to extend your (presumably third party) class, then the TSC will warn you, that there is no such method defined on the class statically.
My workaround was the following little hack:
module MyModule {
export function Dog(){};
}
// in the other file
if (typeof MyModule !== 'undefined'){
Cast<any>(MyModule.Dog).Create = ()=>{return new Dog();};
}
// where Cast is a hack, for TS to forcefully cast types :)
Cast<T>(element:any):T{ return element; }
This should cast MyModule.Dog, to an any object, therefore allowing attachment of any kinds of properties, functions.

TypeScript "use"/alias for Classes

Is there a way to do an alias or "use" (like PHP) for a TypeScript class/module.
Example:
If I have:
module Foo {
class Bar {}
}
Normally I have to write Foo.Bar to use it outside of the module. Is there a way I can alias that to something else, like "FooBar".
This would be really useful if you have several submodules (which my current project does), like:
module A.B.C.D {
export class E {}
}
is normally A.B.C.D.E which is silly.
According to page 82 of the old Typescript language spec, it stated that the following is possible.
So you should be able to alias "use" a module without having to reference the entire hierarchy.
module A.B.C
{
import XYZ = X.Y.Z;
export function ping(x: number) {
if (x > 0) XYZ.pong(x – 1);
}
}
module X.Y.Z
{
import ABC = A.B.C;
export function pong(x: number) {
if (x > 0) ABC.ping(x – 1);
}
}
The new Typescript language spec covers similar material in the section on Import Alias Declarations