Could someone comment on the current status and maturity of the following annotation language constructs, which don't seem to be documented?
interface, implements, and interface (single|multiple) inheritance - As in lib/core.js and elsewhere.[index: number]: File)static (...values:Array<any>): Array<any>)I am working on a WebIDL to Flow converter motivated by #477 (with some relevant discussion in #165 and #140). The basics work, but for a robust conversion I could really use some pointers as to the semantics of the above (and whether it's wise to rely on them at all).
For example, am I correct in assuming that an interface, like type and unlike class, does not result in its identifier added as a property on the global scope? This interpretation is analogous to the effect of [NoInterfaceObject] on a WebIDL interface.
Cool! I'm excited to see the result of this work.
You are correct that interfaces are "compiled away" and don't result in any runtime bindings on their own.
implements is not implemented currently (no pun intended). I'm not sure if anyone is working on it. Can you share some IDL that you would like to translate using it? Maybe we can find alternatives, or possibly try to get it working.
Indexed properties might have some issues surrounding them, but I think it's safe to use them assuming they work properly. Any bugs there we should fix, but I don't think it should block what you're working on.
What do you mean exactly by operator overloading as it relates to static function calls? Overloading in flow doesn't quite work as you might expect coming from TypeScript, but I don't think anyone has spent a lot of time working out the details there. Again, an example of what you might like to generate given IDL would help here.
Overall, I'd recommend that you not let the maturity of features in flow block you from getting this important work done. If anything, your efforts will inspire us to get these things in working order! Thanks!
Thanks for the encouragement, it means a lot :)
Re: Interfaces being "compiled away" - so is this a bug?
interface I {}
type T = {};
new I(); // <--- No error!
new T(); // Identifier T: could not resolve name
It seems to me that implements (and multiple inheritance for interface?) would best represent WebIDL's A implements B; statement (which is defined to mean "all objects that implement A must also implement B"). I don't have data on how often this would come up when translating real-world IDL (but I think it will).
My current workaround is to walk the inheritance/implements DAG and actually copy members from A to B and so on. This also provides a solution for name hiding in the [NoInterfaceObject] case - just emit the respective interface as type rather than class and remove all inheritance/implements references to it, as they're now redundant. I _think_ (more thoughts welcome) that this emulates the WebIDL ECMAScript binding correctly. But the resulting Flow declarations are more verbose and less semantic than they probably could be.
By overloading of the function call operator I mean declarations that make objects callable, such as
declare module "assert" {
declare var exports: {
(value: any, message?: string): void;
}
}
Which seems to fit legacycaller.
More questions:
1) What _about_ overloading? WebIDL supports it for functions, constructors, etc. Does Flow? Or should I calculate the union signatures myself?
2) WebIDL also has _named constructors_, as in:
[NamedConstructor=Audio(optional DOMString src)]
interface HTMLAudioElement : HTMLMediaElement {};
This defines (in addition to any constructor which HTMLAudioElement may or may not expose under its own name) a global constructor named Audio which constructs HTMLAudioElements.
But how do I represent it in Flow? The following seems inadequate.
class HTMLAudioElement {}
declare function Audio(src?: string): HTMLAudioElement;
//...
var a: HTMLAudioElement = new Audio(); // <---- Error
I think you're right about the interface bug. I opened a PR to fix at #481.
I think your solution of copying through the DAG is a great workaround. As long as you also declare the interfaces themselves, so consumers can use them as types. I'll look into implements in the mean time.
Object types can have callable properties, but I'm having trouble defining one on an interface, so I'm not sure that we can support legacycaller for class types right now. Could be very wrong about this.
I'm also not quite sure what level of overloading support flow has right now, but afaik we want to support it. It would be very helpful if you could generate overloaded signatures and file issues for any unexpected behavior that results.
Named constructors do expose an invalid assumption flow makes about constructors. Flow assumes that constructors return void and return objects of the class type. This doesn't always hold: explicit return from the constructor` and this, too. You're also right that just declaring a function won't work, because it needs to be used as a constructor, and constructors can't currently have a different return type.
@popham, you know a lot more about class semantics than I do. Any thoughts about this?
Just wanted to point you towards the repo where I'm working on the converter: motiz88/webidl-to-flow
It's neither feature-complete nor tested nor documented, really, but I may have to take a break from developing it for a few days so I opted to push _something_ in the mean time.
I recall seeing that interface is deprecated in source comments. I'd avoid it like plague. (I vaguely remember seeing that the interface concept was split to type + declare class.)
Whoops. Looks like it hasn't been deprecated yet. parser/parser_flow.ml:
(* TODO: deprecate `interface`; somewhat confusingly, it plays two roles,
which are subsumed by `type` and `declare class` *)
I'm looking for a way to do declare class A extends B implements C, D, E, where B, C, D, E may or may not originate as [NoInterfaceObject] types (=> Flow type, or interface if that stays). For now I will keep emulating this outside of Flow.
On a related note, Flow happily _parses_ multiple types in extends, but immediately discards all but the first. Maybe it should warn/error instead?
declare class B1 {prop1: string}
declare class B2 {prop2: string}
type T = {prop3: string}
declare class D extends B1, B2, T {} // <--- No error - cf. `extends T`
var b1: B1 = new D(); // <--- No type error
var b2: B2 = new D(); // <--- Type error
var t: T = new D(); // <--- prop3 not found in D
Just a status update here. I'm working on full interface support, which will basically do the following:
Wanted to just mention that interface and implements support has landed.
@nmn uh, where exactly? Any doc for them?
Here are the docs for interfaces:
https://flowtype.org/en/docs/types/interfaces/