howto: optional (nested) class property? - interface

I have a class which I want to have some optional nested properties.
class Input {
stuff {
first_name?: string; // optional
};
however it seems that isn't legal typescript. ; expected
Next is to pull stuff out into an interface
interface IFrom {
id: any;
first_name?: string;
};
class Input {
from:IFrom;
however when i put these in the same file I get
tsPublic property 'from' of exported class has or is using private name 'IFrom'.
I can't make public interface
'public' modifier cannot appear on a module element.
What I've ended up doing is putting the interface in yet another file, but this is getting to be astronaut engineering where every struct and property needs to be in its own file...
Am I missing something about how best to do this?
I want the first_name property to be public but part of a struct.
Want it to be optional.
Prefer fewer individual files.
Thanks!

however it seems that isn't legal typescript. ; expected
The syntax for inline types is incorrect. You are missing a :. The following works fine:
class Input {
stuff: {
first_name?: string; // optional
};
}
That answers the question. But since you tried more stuff:
Public property 'from' of exported class has or is using private name 'IFrom'.
You probably have export class ... that means that you need to do export interface too to export any interface that the class uses.

You should use the keyword export with interfaces and classes, not public.
Here is an example:
module ModuleA {
export interface IFrom {
id: any;
first_name?: string;
}
}
module ModuleB {
export class Input {
from:ModuleA.IFrom;
}
}
var input = new ModuleB.Input();
input.from = {id: 123, first_name: 'Bob'};
alert(input.from.first_name); // Bob

Related

Typescript - Get uninitialized properties after compilation

I am currently writing a wrapper around socket.io. Comming from a very object-oriented background, I want to implement the concept of Models in my framework/wrapper.
If you happen to know socket.io you might know that you get the data that is associated with an event as a parameter, now I have implemented a custom routing system where the handler of the route gets the data in an express.js like request object.
The idea is to have model classes that look something like this:
class XRequestModel
#v.String({ message: 'The username must be a string!' })
public userName: string;
}
And the route event might look something like this:
#RouteConfig({ route: '/something', model: XRequestModel })
class XEvent extends Route {
public on(req: Request<XRequestModel>, res: Response) {
// Handle Event
}
}
And to complete the example here is how the request object might look like:
class Request<T> {
public data: T;
}
Now generics in typescript are very limited since the type information is removed after compilation, I can not use the generic Request parameter ( which is the type of the model ) to get metadata from the model - Metadata, in this case, is the validation decorator. To overcome this issue I give a reference of the Model class to the RouteConfig of the RouteEvent, which is internally used and would allow me to create instances of the model, get the properties and so on...
The idea here is to give the handler of a route, a request object with pre-validated, typesafe data.
The thing holding me back from this, is the fact that unused properties, get removed after compilation by typescript, So I cannot get the metadata of the model. Initializing the class-property would solve this:
class XRequestModel
#v.String({ message: 'The username must be a string!' })
public userName: string = '';
}
But I think this makes for some very verbose syntax, and I dont want to force the user of this wrapper to init all the model properties.
An implementation side-note:
The user of the framework has to register the classes to a 'main' class and from there I can get the Route-class via decorator reflection.
When I try to get the properties of the model without initialized properties - First model example.
// Here the route.config.model refers to the model from the RouteConfig
Object.getOwnPropertyNames(new route.config.model());
>>> []
Here is what I get with initialized properties:
Object.getOwnPropertyNames(new route.config.model());
>>> [ 'userName' ]
Here a link to the GitHub repository: https://github.com/FetzenRndy/SRocket
Note that models are not implemented in this repo yet.
Basically, my question is: How can I get the properties of a class that has uninitialized properties after compilation.
The problem is that if no initialization happens, no code is emitted for the fields, so at runtime the field does not exist on the object until a value is assigned to it.
The simplest solution would be to initialize all fields even if you do so with just null :
class XRequestModel {
public userName: string = null;
public name: string = null;
}
var keys = Object.getOwnPropertyNames(new XRequestModel())
console.log(keys); // [ 'userName', 'name' ]
If this is not a workable solution for you, you can create a decorator that adds to a static field on the class and the walk up the prototype chain to get all fields:
function Prop(): PropertyDecorator {
return (target: Object, propertyKey: string): void => {
let props: string[]
if (target.hasOwnProperty("__props__")) {
props = (target as any)["__props__"];
} else {
props = (target as any)["__props__"] = [];
}
props.push(propertyKey);
};
}
class XRequestModelBase {
#Prop()
public baseName: string;
}
class XRequestModel extends XRequestModelBase {
#Prop()
public userName: string;
#Prop()
public name: string;
}
function getAllProps(cls: new (...args: any[]) => any) : string[] {
let result: string[] = [];
let prototype = cls.prototype;
while(prototype != null) {
let props: string[] = prototype["__props__"];
if(props){
result.push(...props);
}
prototype = Object.getPrototypeOf(prototype);
}
return result;
}
var keys = getAllProps(XRequestModel);
console.log(keys);

TypeScript: Access global type hidden by local class definition?

Lets assume i have a class which has the same name as an previously defined type which is defined inside lib.d.ts. How would i make use of that type within this class.
For example, i have the class Event, which has to deal with the browsers Event object, which is defined as an interface in lib.d.ts.
export class Event { // own definition of Event which hides original Event
public dealWithBrowserEvent(event: Event): void { // Event object defined by lib.d.ts
// deal with it
}
}
How would i tell Typescript that this are two different types. Of course i could simply rename my class, but i don't want to do that, because the name is perfect for my use case.
You can archive this by doing so:
E.ts:
class CustomEvent
{
public dealWithBrowserEvent(event: Event): void
{
}
}
export default CustomEvent;
A.ts:
import Event from './E'
export class SomeClass
{
//Parameter e here is instance of your CustomEvent class
public someMethod(e: Event): void
{
let be: any;
//get browser event here
e.dealWithBrowserEvent(be)
}
}
More on declaration merging, and what can be merged and what not: link
I strongly recommend you not doing so. This code will lead to a lot of confusion for your colleagues reading/modifying it later, let alone headache of not being able to use in the same file standard class for Event.
In the meantime i found a quite doable solution. I defined an additional module which exports renamed interfaces. If i import this module, i can use the renamed types as if they would be original types.
browser.ts
// Event will be the one from lib.d.ts, because the compiler does not know anything about
// the class Event inside this module/file.
// All defined properties will be inherited for code completion.
export interface BrowserEvent extends Event {
additionalProperty1: SomeType;
additionalProperty2: SomeType;
}
If you don't need additional properties you can just do type aliasing:
browser.ts
// Alias for Event
export type BrowserEvent = Event;
event.ts
import {BrowserEvent} from './browser.ts';
export class Event { // Definition of Event hides original Event, but not BrowserEvent
public dealWithBrowserEvent(event: BrowserEvent): void {
// deal with it
}
}
I'm quite happy with this solution, but maybe there is a even better solution.

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;