Typescript: Cannot export a module that is a generic interface and contains other generic interfaces - class

I'm trying to write a CommonJS declaration file for Bluebird, a promise library that directly exports a generic Promise class. However, the library also exports several other generic classes as static members (PromiseInspection), and it seems like its impossible to model this with typescript.
Edit: Usage example, to illustrate how the module's exported class works:
import Promise = require('bluebird');
var promise:Promise<number> = Promise.cast(5);
var x:Promise.PromiseInspection<number> = promise.inspect();
I tried several strategies - simplified examples follow:
1. The obvious way
declare module "bluebird" {
class PromiseInspection<T> {
// ...
}
class Promise<T> {
PromiseInspection: typeof PromiseInspection; // error
constructor<T>();
inspect():PromiseInspection<T>; // error
static cast<U>(value:U):Promise<U>;
// ...
}
export = Promise;
}
Fails with the error unable to use private type PromiseInspection as a public property
2. Using a static interface
declare module "bluebird2" {
interface PromiseInspection<T> {
// ...
}
interface Promise<T> {
constructor<T>();
inspect():PromiseInspection<T>;
}
interface PromiseStatic {
new<T>();
PromiseInspection:typeof PromiseInspection;
cast<U>(value:U):Promise<U>; // error
}
export = PromiseStatic;
}
Also fails similarly, but this time the private type is Promise
3. Trying to directly export a constructor function from the module
declare module "bluebird3" {
export interface PromiseInspection<T> {
// ...
}
export interface Promise<T> {
constructor<T>();
inspect():PromiseInspection<T>;
}
export new<T>(); // syntax error
export function cast<U>(value:U):Promise<U>;
}
This almost works, except of course its impossible to a constructor function that way.
4. The namespace polluting way (Works, with downsides)
interface PromiseInspection<T> {
// ...
}
interface Promise<T> {
constructor<T>();
inspect():PromiseInspection<T>;
}
declare module "bluebird4" {
interface PromiseStatic {
new<T>():Promise<T>;
PromiseInspection: typeof PromiseInspection;
cast<U>(value:U):Promise<U>;
}
export = PromiseStatic;
}
Works, but it pollutes the global namespace with both Promise and PromiseInspection. This might be okay but I'd rather avoid it as in CommonJS its usually considered unacceptable.
5. With declaration merging (gets me 90% of the way...)
declare module "bluebird5" {
module Promise {
export interface PromiseInspection<T> {
value(): T;
// ...
}
export
function cast<U>(value: U): Promise<U> ;
}
class Promise<T> {
new <T> (): Promise <T> ;
inspect(): Promise.PromiseInspection <T> ;
}
export = Promise;
}
Almost there - except that now I'm not allowed to replace class Promise<T> with interface Promise<T>, making Promise<T> unextendable. If I try to do it, the following code:
import Promise = require('bluebird');
var x = new Promise<number>();
x.inspect().value().toExponential();
fails with the error "Invalid 'new' expression"
Link to the actual, work-in-progress bluebird.d.ts - this one currently pollutes the global namespace (uses solution 4)
Is there a better way to do this, or did I hit a language limitation?

Anders Hejlsberg posted an answer on CodePlex, so I'm going to add it here. The declaration merging solution was close - but I also needed a "var" declaration to declare the static interface as it is the only one that can accept a constructor function.
declare module "bluebird" {
module Promise {
export interface PromiseInspection<T> {
value(): T;
}
}
interface Promise<T> {
inspect(): Promise.PromiseInspection <T> ;
}
var Promise: {
new<U>(): Promise<U>;
cast<U>(value: U): Promise<U> ;
}
export = Promise;
}
So basically:
interface members in the module declaration (as long as they declare just types i.e. non-physical)
instance members in the main interface
static function members, the constructor and other "physical" members in the var declaration.
Also, his comment:
Writing it this way you have a separate declaration for each of the three meanings of the identifier Promise: As a namespace (a module containing only types), as a type (that happens to be generic), and as a value.

Looking at your code I noticed you were missing a few export statements. The code below compiles - would it suit?
declare module bluebird {
export class PromiseInspection<T> {
// ...
}
export class Promise<T> {
constructor<T>();
inspect():PromiseInspection<T>;
static all<T>(promises:Promise<T>[]):Promise<T[]>;
}
}
declare module "bluebird" {
export = bluebird;
}
Though I generally favour using interfaces when defining typings as in #2:
declare module bluebird {
export interface PromiseInspection<T> {
// ...
}
export interface Promise<T> {
constructor<T>();
inspect():PromiseInspection<T>;
}
export interface PromiseStatic {
new<T>();
all<T>(promises:Promise<T>[]):Promise<T[]>;
}
}
declare module "bluebird" {
export = bluebird;
}
Failing that have you tried using another promises library as the basis for your typings? You could do worse than look at https://github.com/borisyankov/DefinitelyTyped/blob/master/q/Q.d.ts
Roughly speaking they look a little like this:
declare function Q<T>(promise: Q.IPromise<T>): Q.Promise<T>;
declare function Q<T>(promise: JQueryPromise<T>): Q.Promise<T>;
declare function Q<T>(value: T): Q.Promise<T>;
declare module Q {
//… functions etc in here
}
declare module "q" {
export = Q;
}

Related

Limit autocompletion of macro function when used as a static extension to multiple types

When using a static macro function, meant to be used as a static extension, how can I limit types of variables that will get this function on an autocompletion list? Caveat: I know I can use ExprOf<T> but I need this for multiple types to check inside my macro if expr unifies with a specific abstract.
Besides leveraging the type system to perform that unification by itself, if possible, you might be able to use a temporary abstract exclusively for this "filtering".
// exclusively for static extension x autocomplete
private abstract PseudoType(Dynamic)
from ActualType1
from ActualType2
from ActualType3 {}
[...]
public static macro function myMacro(value:ExprOf<PseudoType>}
{
// ExprOf doesn't do anything other than help with autocomplete
// do actual unification here
// return the appropriate result
}
[EDIT] here's an example (live on Try Haxe/alt.):
Macro.hx:
import haxe.macro.Expr;
private abstract PseudoType(Dynamic)
from String
from Int
from { val:Float } {}
class Macro {
public static macro function magic(value:ExprOf<PseudoType>)
{
return macro Std.string($value);
}
}
Test.hx:
using Macro;
class Test {
static function main()
{
trace("Haxe is great!".magic());
trace(42.magic());
trace({ val : 3.14 }.magic());
}
}

How to properly bind current object context in ES6 using babelify

I'm trying to bind current instance to the class method, please note ES6 syntax.
class SomeClass {
search() => { ... }
}
Which is 100% legit code, however, babelify doesn't want to compile it
SyntaxError: /Users/vladmiller/Projects/test/test/client/test/app/pages/Search.react.js: Unexpected token (50:26) while parsing file: /Users/vladmiller/Projects/test/test/client/test/app/pages/Search.react.js\
Instead, now I have to bind context in class constructor
class SomeClass {
constructor() {
this.search = this.search.bind(this)
}
search() { ... }
}
Which is quite annoying and boring.
UPD: It turns out that this is invalid ES6 syntax; therefore the question is follows. What is the best way to bind instance context to a class method?
UPD2: By default context should be attached, however, the issue with React http://jsbin.com/citafaradu/2/edit?js,console,output
This code is not valid ES2015. Prototype methods are defined like this:
class SomeClass {
search() { /* ... */ }
}

TypeScript 0.9.1 CommonJS: correctly declaring exported ambient class implementing external interface?

EDIT:
Put another way the following within a .d.ts file shouldn't produce compiler error TS2137 'Class "MyClass" does not implement interface "IInterface"':
interface IInterface {
someMethod():void;
}
declare module "mod" {
export class MyClass implements IInterface {
constructor();
}
}
because I'm not (and can't in a declaration) implementing anything. Is this a bug in the compiler or is there some other way/syntax to do what the above implies? I would think the compiler smart enough to know to precisely include IInterface's signature as part of MyClass, and not require its methods be redeclared.
ORIGINAL:
I'm trying to write a d.ts for the node component bunyan. Having a problem with exporting a class that implements an external interface, specifically RingBuffer which extends node's EventEmitter. The problem simplified is (below in a bunyan.d.ts file):
// this interface declared in <reference..., put inline here for simplicity
interface IExternal {
inheritedMethod():void;
}
interface RingBuffer extends IExternal {
write():void;
}
declare var RingBuffer: {
new():RingBuffer;
}
declare module "bunyan" {
export var RingBuffer;
}
then used in myNodeApp.js
/// <references path="bunyan.d.ts" />
import bunyan = require( 'bunyan' );
var rb = new bunyan.RingBuffer();
// compiler doesn't error on this; thinks RingBuffer is type any.
// also, no intellisense to show write() method.
rb.badFunc();
changing bunyan.d.ts to:
declare module "bunyan" {
export class RingBuffer { constructor(); }
}
compiles, but same problem when used; no intellisense, no compile errors.
changing bunyan.d.ts to
declare module "bunyan" {
export var RingBuffer:RingBuffer;
}
causes compile error in myNodeApp.js
// error TS2083: Invalid 'new' expression
import rb = new bunyan.RingBuffer();
removing from bunyan.d.ts
declare module "bunyan" {
...
}
causes compile error in myNodeApp.js
// error TS2071: Unable to resolve external module ''bunyan''
import bunyan = require( 'bunyan' );
changing bunyan.d.ts
interface IExternal {
inheritedMethod():void;
}
interface IRingBuffer extends IExternal {
}
declare module "bunyan" {
export class RingBuffer implements IRingBuffer {}
}
cause compile error
// error TS2137: Class "bunyan".RingBuffer declares interface IRingBuffer but
// does not implement it: type '"bunyan".RingBuffer' is missing property
// 'inheritedMethod' from type 'IRingBuffer'
implying I have to redeclare all inherited methods from all extended interfaces, besides IRingBuffer, which seems a bit ridiculuous to have to do in a d.ts file
Does anyone know the 'correct' way to declare an ambient class that implements an interface for consumption in another CommonJS module??
An alternate way to define it would be the way Jquery's typescript definition is defined. You have separate interfaces for static and instance members. Here is a sample complete definition:
interface IExternal {
inheritedMethod():void;
}
interface IRingBuffer extends IExternal {
write():void;
}
// Static functions and constructors
interface IRingBufferStatic{
new():IRingBuffer;
}
declare var RingBuffer:IRingBufferStatic;
declare module "bunyan" {
export var RingBuffer:IRingBufferStatic;
}
// In the second file
import bunyan = require( 'bunyan' );
var rb = new bunyan.RingBuffer();
// you get an error here
rb.badFunc();
Try it online

How do I create a macro for property extensions

I'd like to write my own macro for creating property like objects in Haxe.
This question is not so much about properties but more about writing macros.
(probably NME has already a macro for that).
having this class in haxe
class Foo {
#:property var bar:String;
}
I like this to be expanded into
class Foo {
private var bar:String;
public function setBar(_val:String):void {
this.bar = _val;
}
public function getBar():String {
return this.bar;
}
}
I read the corresponding docs but honestly I find them very confusing.
thanks
You might want to take a look at how tinkerbell resolves the same issue: https://github.com/back2dos/tinkerbell/wiki/tink_lang#wiki-accessors
This Type Builder example (pasted below for reference, but there's better description at the link) found in the Haxe Manual is a nice, simple example of adding a function to a Class.
Adding a property would be much the same. I added a trace(field) loop to help get a feel for how they're defined:
Main.hx
#:build(TypeBuildingMacro.build("myFunc"))
class Main {
static public function main() {
trace(Main.myFunc); // my default
}
}
TypeBuildingMacro.hx
import haxe.macro.Context;
import haxe.macro.Expr;
class TypeBuildingMacro {
macro static public function build(fieldName:String):Array<Field> {
var fields = Context.getBuildFields();
for (field in fields) { trace(field); }
var newField = {
name: fieldName,
doc: null,
meta: [],
access: [AStatic, APublic],
kind: FVar(macro : String, macro "my default"),
pos: Context.currentPos()
};
fields.push(newField);
return fields;
}
}
Note that Main.hx must invoke the macro with the #:build metadata, so the compiler knows to run the macro (which adds the function) before processing the Main class itself.

TypeScript module and class simultaneously?

I am currently trying to build a TypeScript definition file for OpenLayers.
The problem is that in OpenLayers there are certain classes that would translate to both a module and a class in TypeScript.
For example there is the Protocol class in module OpenLayers and there is a class Response in module OpenLayers.Protocol.
How could I model that in TypeScript? Can I make Protocol a class and define the Response class as a inner class that is exported? How would you go about solving that problem?
Declare Response as a static field of Protocol with a constructor type, returning an interface that defines the Response class:
declare module OpenLayers {
export interface IProtocolResponse {
foo(): void;
}
export class Protocol {
static Response: new () => IProtocolResponse;
}
}
var response = new OpenLayers.Protocol.Response();
response.foo();
Edit:
Or as Anders points out in this discussion list question, you can have multiple constructors for the inner class in this way:
declare module OpenLayers {
export interface IProtocolResponse {
foo(): void;
}
export class Protocol {
static Response: {
new (): IProtocolResponse;
new (string): IProtocolResponse;
};
}
}
var response = new OpenLayers.Protocol.Response('bar');
response.foo();
The main downside of both approaches is that you cannot derive a class from OpenLayers.Protocol.Response.
Here is my updated answer, which I hope helps - it should get you started on defining OpenType:
declare module OpenType {
export class Protocol {
constructor();
Request;
}
}
var x = new OpenType.Protocol();
var y = new x.Request();
var z = x.Request;