TypeScript - passing a class as an argument, and reflection - reflection

I am writing a generic unmarshaller. It converts graph DB data to generated TypeScript (1.8.7) model classes. The input is JSON. The output should be an instance of a model class.
My ultimate goal is to create something like Hibernate OGM, only for Tinkerpop Frames and TypeScript, with REST endpoint in the middle.
What's the right way to pass a class as a parameter and reach it's static members? I want to have something like this:
SomeModel some = <SomeModel> unmarshaller.fromJSON({/*Object from JSON*/}, SomeModel);
I've tried to write a method.
Not sure if I am heading in the right direction, feel free to suggest different approaches.
public fromJSON(input: Object, clazz: typeof FrameModel): FrameModel
{
// This only demonstrates access to Framemodel's metadata
// about original Java model classes.
clazz.graphPropertyMapping;
clazz.graphRelationMapping;
let result = {};
...
return result;
}
...
But when I tried to execute this on Plunker, I got execution errors with unuseful stacktrace.
The model superclass looks like this:
/**
* Things common to all Frames models on the Typescript side.
*/
export class FrameModel
{
// Model metadata
static discriminator: string;
static graphPropertyMapping: { [key:string]:string; };
static graphRelationMapping: { [key:string]:string; };
// Each instance needs a vertex ID
private vertexId: number;
public getVertexId(): number {
return this.vertexId;
}
}
Sample model class:
import {TestPlanetModel} from './TestPlanetModel';
import {TestShipModel} from './TestShipModel';
export class TestGeneratorModel extends FrameModel
{
static discriminator: string = 'TestGenerator';
static graphPropertyMapping: { [key:string]:string; } = {
bar: 'boo',
name: 'name',
rank: 'rank',
};
static graphRelationMapping: { [key:string]:string; } = {
colonizes: 'colonizedPlanet',
commands: 'ship',
};
boo: string;
name: string;
rank: string;
public colonizedPlanet: TestPlanetModel[]; // edge label 'colonizedPlanet'
public ship: TestShipModel; // edge label 'ship'
}
I haven't found much material on reflection and class handling in TypeScript.
I know how I would do this in Java.
I know how I would do this in JavaScript.
I understand that I might achieve similar results with decorators, but having fields or static fields seemed a bit simpler, for generated models.

You've maybe already noticed that class members cannot have const keyword. But you could go with static instead. Also member should be public if you want it to be accessible from outside world.
public static graphPropertyMapping: { [key:string]:string; } = {
bar: 'boo',
name: 'name',
rank: 'rank',
};
As for creating result instance:
let result = new clazz();
//copy properties
return result;

If I understand you correctly then here's something to help you get started:
interface Model {}
interface ModelData {}
interface MyModelConstructor<M extends Model, D extends ModelData> {
new(data: D): M;
// static members
graphPropertyMapping: any;
graphRelationMapping: any;
}
class Unmarshaller {
public fromJSON<T>(input: string | ModelData, ctor: MyModelConstructor<T, ModelData>): T {
let data: ModelData = (typeof input === "string") ? JSON.parse(input) : input;
let propertyMapping = ctor.graphPropertyMapping;
let relationMapping = ctor.graphRelationMapping;
// do whatever with the mappings
return new ctor(input);
}
}
(code in playground)
I don't know how your models look like, so I hope this is close enough.

I recently released an enhanced version of the TypeScript compiler that allows exactly what you are expecting: read all (static or not) fields metadata from a class. For example you can write:
interface MyInterface {
active:boolean;
description: string;
}
class MyClass {
id: number;
name: string;
myComplexField: MyInterface;
}
function printMembers(clazz: Class) {
let fields = clazz.members.filter(m => m.type.kind !== 'function'); //exclude methods.
for(let field of fields) {
let typeName = field.type.kind;
if(typeName === 'class' || typeName === 'interface') {
typeName = (<Class | Interface>field.type).name;
}
console.log(`Field ${field.name} of ${clazz.name} has type: ${typeName}`);
}
}
printMembers(MyClass.getClass());
this is the output:
$ node main.js
Field id of MyClass has type: number
Field name of MyClass has type: string
Field myComplexField of MyClass has type: MyInterface
Of course, if you change the members property access of clazz to statics you will retrieve all static members. These information can be accessed at coding time too, so you can use autocompletion.
You can do the same with Interfaces metadata. Simply write MyInterface for example, and access its members.
You can find the project here.

Related

TypeScript Compiler API: How to get type with resolved type arguments?

I want to merge class declarations in a .dt.s file to generate a cleaner public API. I am stuck on how to make this work with generic type arguments. Let's say I have:
class A1<T> { // Non-exported class I want to hide
data?: T;
}
export class B1 extends A1<string> {
}
Ideally, I want to turn this into:
export class B1 {
data?: string;
}
I can get the type of A1 and then copy its members. But how do get a resolved version of A1 that uses string instead of T?
For reference, this is my current code:
for (const heritageClause of node.heritageClauses) {
for (const type of heritageClause.types) {
if (isExported(type.modifiers)) {
exportedTypes.push(type);
} else {
const privateType = typeChecker.getTypeAtLocation(type);
if (privateType?.symbol?.members) {
privateType.symbol.members.forEach((definition, memberName) => {
if (!currentMembers || !currentMembers.has(memberName)) {
additionalMembers.push(...definition.declarations);
}
}
});
}
}
}
I believe the method you are looking for is TypeChecker#getTypeOfSymbolAtLocation(symbol, node).
The following should get the resolved type of string | undefined:
// by the way, recommend renaming `type` to `typeNode` to avoid confusion
typeChecker.getTypeOfSymbolAtLocation(privateType.getProperties()[0], type);

Haxe: Binding pattern with abstract fields access methods

I'd like to make wrapper to implement simple data binding pattern -- while some data have been modified all registered handlers are got notified. I have started with this (for js target):
class Main {
public static function main() {
var target = new Some();
var binding = new Bindable(target);
binding.one = 5;
// binding.two = 0.12; // intentionally unset field
binding.three = []; // wrong type
binding.four = 'str'; // no such field in wrapped class
trace(binding.one, binding.two, binding.three, binding.four, binding.five);
// outputs: 5, null, [], str, null
trace(target.one, target.two, target.three);
// outputs: 5, null, []
}
}
class Some {
public var one:Int;
public var two:Float;
public var three:Bool;
public function new() {}
}
abstract Bindable<TClass>(TClass) {
public inline function new(source) { this = source; }
#:op(a.b) public function setField<T>(name:String, value:T) {
Reflect.setField(this, name, value);
// TODO notify handlers
return value;
}
#:op(a.b) public function getField<T>(name:String):T {
return cast Reflect.field(this, name);
}
}
So I have some frustrating issues: interface of wrapped object doesn't expose to wrapper, so there's no auto completion or strict type checking, some necessary attributes can be easily omitted or even misspelled.
Is it possible to fix my solution or should I better move to the macros?
I almost suggested here to open an issue regarding this problem. Because some time ago, there was a #:followWithAbstracts meta available for abstracts, which could be (or maybe was?) used to forward fields and call #:op(a.b) at the same time. But that's not really necessary, Haxe is powerful enough already.
abstract Binding<TClass>(TClass) {
public function new(source:TClass) { this = source; }
#:op(a.b) public function setField<T>(name:String, value:T) {
Reflect.setField(this, name, value);
// TODO notify handlers
trace("set: $name -> $value");
return value;
}
#:op(a.b) public function getField<T>(name:String):T {
trace("get: $name");
return cast Reflect.field(this, name);
}
}
#:forward
#:multiType
abstract Bindable<TClass>(TClass) {
public function new(source:TClass);
#:to function to(t:TClass) return new Binding(t);
}
We use here multiType abstract to forward fields, but resolved type is actually regular abstract. In effect, you have completion working and #:op(a.b) called at the same time.
You need #:forward meta on your abstract. However, this will not make auto-completion working unless you remove #:op(A.B) because it shadows forwarded fields.
EDIT: it seems that shadowing happened first time I added #:forward to your abstract, afterwards auto-completion worked just fine.

Adding observer for KVO without pointers using Swift

In Objective-C, I would normally use something like this:
static NSString *kViewTransformChanged = #"view transform changed";
// or
static const void *kViewTransformChanged = &kViewTransformChanged;
[clearContentView addObserver:self
forKeyPath:#"transform"
options:NSKeyValueObservingOptionNew
context:&kViewTransformChanged];
I have two overloaded methods to choose from to add an observer for KVO with the only difference being the context argument:
clearContentView.addObserver(observer: NSObject?, forKeyPath: String?, options: NSKeyValueObservingOptions, context: CMutableVoidPointer)
clearContentView.addObserver(observer: NSObject?, forKeyPath: String?, options: NSKeyValueObservingOptions, kvoContext: KVOContext)
With Swift not using pointers, I'm not sure how to dereference a pointer to use the first method.
If I create my own KVOContext constant for use with the second method, I wind up with it asking for this:
let test:KVOContext = KVOContext.fromVoidContext(context: CMutableVoidPointer)
EDIT: What is the difference between CMutableVoidPointer and KVOContext? Can someone give me an example how how to use them both and when I would use one over the other?
EDIT #2: A dev at Apple just posted this to the forums: KVOContext is going away; using a global reference as your context is the way to go right now.
There is now a technique officially recommended in the documentation, which is to create a private mutable variable and use its address as the context.
(Updated for Swift 3 on 2017-01-09)
// Set up non-zero-sized storage. We don't intend to mutate this variable,
// but it needs to be `var` so we can pass its address in as UnsafeMutablePointer.
private static var myContext = 0
// NOTE: `static` is not necessary if you want it to be a global variable
observee.addObserver(self, forKeyPath: …, options: [], context: &MyClass.myContext)
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
if context == &myContext {
…
}
else {
super.observeValue(forKeyPath: keyPath, of: object, change: change, context: context)
}
}
Now that KVOContext is gone in Xcode 6 beta 3, you can do the following. Define a global (i.e. not a class property) like so:
let myContext = UnsafePointer<()>()
Add an observer:
observee.addObserver(observer, forKeyPath: …, options: nil, context: myContext)
In the observer:
override func observeValueForKeyPath(keyPath: String!, ofObject object: AnyObject!, change: [NSObject : AnyObject]!, context: UnsafePointer<()>) {
if context == myContext {
…
} else {
super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context)
}
}
Swift 4 - observing contentSize change on UITableViewController popover to fix incorrect size
I had been searching for an answer to change to a block based KVO because I was getting a swiftlint warning and it took me piecing quite a few different answers together to get to the right solution. Swiftlint warning:
Block Based KVO Violation: Prefer the new block based KVO API with keypaths when using Swift 3.2 or later. (block_based_kvo).
My use case was to present a popover controller attached to a button in a Nav bar in a view controller and then resize the popover once it's showing - otherwise it would be too big and not fitting the contents of the popover. The popover itself was a UITableViewController that contained static cells, and it was displayed via a Storyboard segue with style popover.
To setup the block based observer, you need the following code inside your popover UITableViewController:
// class level variable to store the statusObserver
private var statusObserver: NSKeyValueObservation?
// Create the observer inside viewWillAppear
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
statusObserver = tableView.observe(\UITableView.contentSize,
changeHandler: { [ weak self ] (theTableView, _) in self?.popoverPresentationController?.presentedViewController.preferredContentSize = theTableView.contentSize
})
}
// Don't forget to remove the observer when the popover is dismissed.
override func viewDidDisappear(_ animated: Bool) {
if let observer = statusObserver {
observer.invalidate()
statusObserver = nil
}
super.viewDidDisappear(animated)
}
I didn't need the previous value when the observer was triggered, so left out the options: [.new, .old] when creating the observer.
Update for Swift 4
Context is not required for block-based observer function and existing #keyPath() syntax is replaced with smart keypath to achieve swift type safety.
class EventOvserverDemo {
var statusObserver:NSKeyValueObservation?
var objectToObserve:UIView?
func registerAddObserver() -> Void {
statusObserver = objectToObserve?.observe(\UIView.tag, options: [.new, .old], changeHandler: {[weak self] (player, change) in
if let tag = change.newValue {
// observed changed value and do the task here on change.
}
})
}
func unregisterObserver() -> Void {
if let sObserver = statusObserver {
sObserver.invalidate()
statusObserver = nil
}
}
}
Complete example using Swift:
//
// AppDelegate.swift
// Photos-MediaFramework-swift
//
// Created by Phurg on 11/11/16.
//
// Displays URLs for all photos in Photos Library
//
// #see http://stackoverflow.com/questions/30144547/programmatic-access-to-the-photos-library-on-mac-os-x-photokit-photos-framewo
//
import Cocoa
import MediaLibrary
// For KVO: https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html#//apple_ref/doc/uid/TP40014216-CH7-ID12
private var mediaLibraryLoaded = 1
private var rootMediaGroupLoaded = 2
private var mediaObjectsLoaded = 3
#NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
#IBOutlet weak var window: NSWindow!
var mediaLibrary : MLMediaLibrary!
var allPhotosAlbum : MLMediaGroup!
func applicationDidFinishLaunching(_ aNotification: Notification) {
NSLog("applicationDidFinishLaunching:");
let options:[String:Any] = [
MLMediaLoadSourceTypesKey: MLMediaSourceType.image.rawValue, // Can't be Swift enum
MLMediaLoadIncludeSourcesKey: [MLMediaSourcePhotosIdentifier], // Array
]
self.mediaLibrary = MLMediaLibrary(options:options)
NSLog("applicationDidFinishLaunching: mediaLibrary=%#", self.mediaLibrary);
self.mediaLibrary.addObserver(self, forKeyPath:"mediaSources", options:[], context:&mediaLibraryLoaded)
NSLog("applicationDidFinishLaunching: added mediaSources observer");
// Force load
self.mediaLibrary.mediaSources?[MLMediaSourcePhotosIdentifier]
NSLog("applicationDidFinishLaunching: done");
}
override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) {
NSLog("observeValue: keyPath=%#", keyPath!)
let mediaSource:MLMediaSource = self.mediaLibrary.mediaSources![MLMediaSourcePhotosIdentifier]!
if (context == &mediaLibraryLoaded) {
NSLog("observeValue: mediaLibraryLoaded")
mediaSource.addObserver(self, forKeyPath:"rootMediaGroup", options:[], context:&rootMediaGroupLoaded)
// Force load
mediaSource.rootMediaGroup
} else if (context == &rootMediaGroupLoaded) {
NSLog("observeValue: rootMediaGroupLoaded")
let albums:MLMediaGroup = mediaSource.mediaGroup(forIdentifier:"TopLevelAlbums")!
for album in albums.childGroups! {
let albumIdentifier:String = album.attributes["identifier"] as! String
if (albumIdentifier == "allPhotosAlbum") {
self.allPhotosAlbum = album
album.addObserver(self, forKeyPath:"mediaObjects", options:[], context:&mediaObjectsLoaded)
// Force load
album.mediaObjects
}
}
} else if (context == &mediaObjectsLoaded) {
NSLog("observeValue: mediaObjectsLoaded")
let mediaObjects:[MLMediaObject] = self.allPhotosAlbum.mediaObjects!
for mediaObject in mediaObjects {
let url:URL? = mediaObject.url
// URL does not extend NSObject, so can't be passed to NSLog; use string interpolation
NSLog("%#", "\(url)")
}
}
}
}

structuremap enrichwith not overriden

I am trying to find a way to override a structuremap registry statement containing EnrichWith like so (here is the Registry class):
public class MyRegistry : Registry
{
public MyRegistry()
{
For(typeof(IMyList<int>)).EnrichWith(x => DecorateMyList(x)).Use(typeof(MyListA<int>));
For(typeof(IMyList<int>)).Use(typeof(MyListB<int>));
For<IMyList<string>>().Use<MyListA<string>>();
For<IMyList<string>>().Use<MyListB<string>>();
}
private object DecorateMyList(object o)
{
var genericParameters = o.GetType().GetGenericArguments();
var myListDecoratorType = typeof(MyListDecorator<>).MakeGenericType(genericParameters);
var decorated = Activator.CreateInstance(myListDecoratorType, new []{o});
return decorated;
}
}
public class MyRegistryUser
{
ObjectFactory.GetInstance<IMyList<string>>(); // Good: Returns an instance of MyListB<string> as expected
ObjectFactory.GetInstance<IMyList<int>>(); // Bad: Returns an instance of the decorator containing MyListB<int> - my second rule should have overridden the EnrichWith as well.
}
Am I right to think that there is a glitch in structure map or is there something I'm not seeing?
Thanks in advance

flex inheritance class share

//Base.as
public class Base
{
private var _foo:String;
[Bindable]
public function set foo(value:String):void
{
_foo = value;
}
public function get foo():String
{
return _foo;
}
/*
Many many setter/getter, methods, events
*/
}
//Control.as
public class MyControl extends Group
{
public function MyControl()
{
}
}
//Window.as
public class MyWindow extends spark.components.Window
{
public function MyWindow()
{
}
}
//Module
public class MyModule extends spark.modules.Module
{
public function MyModule()
{
}
}
I want to expose (friendly) Base properties, methods and events on the other classes. Something like this:
var window:MyWindow = new MyWindow();
window.foo = 'Hello World!';
var module:MyModule = new MyModule();
module.foo = 'bar';
<namespace:MyControl foo="Hello World!"/>
I don't want define all the properties in each class because they are many and the same for all of them.
Ideally would define something like:
public class MyControl extends Group, Base
{
public function MyControl()
{
}
}
(I know it can't be done.)
Thanks!
UPDATE:
Thanks again!
Maybe this clarify more my need... On business layer I have a variable called processID (and businessID, operationID, localityID, etc.) what be passed to Window from Menu, and Window passes it to Module. On Module Container, I have a CustomComponent what query database using this variable as parameter. This applied for all (almost) Components on Module. These variables are defined as level business layer, then I define a Class to store and manage these variables (and some related methods operating with these variables using business logic), so I can make a standalone class (or library) for every environment to reusing my common components. The idea is... insert a new CustomComponent and set these variables via mxml, like this:
<custom:MyCustomComponent id="zzz" processID="{processID}" businessID="{businessID}"/>
Module has the business logic for set (o not) any of the variables.
Otherwise, I would have to implement different logic for the CustomComponent (and Module) for read parent's variables and define these variables only in MyWindow (using composite pattern).
You can get your answer from following link -
http://flexinonroids.wordpress.com/2009/05/27/flex-3-dynamically-loading-components-at-runtime/
http://thecomcor.blogspot.in/2007/11/adobe-flex-dynamically-loading-classes.html
Or you can follow below approach -
1) Create an Interface as base
2) Extend your class with interface
3) Load class at runtime with SWFLoader.loaderContext.applicationDomain.getDefinition method
Thanks,
Varun
You can place your classes that require friendly access in the same package as your Base class, and define private fields without any access modifier( it is equivalent to internal modifier).
Otherwise, you can define your namespace like that:
namespace my_internal;
and then define class members like that:
my_internal var _foo:String;
after that, those members will be hidden for all code, except for code that contains
use namespace my_internal;
You can read more here:
http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS5b3ccc516d4fbf351e63e3d118a9b90204-7f9e.html#WS5b3ccc516d4fbf351e63e3d118a9b90204-7f91
However, using 'friend access' can be an evidence of bad design, so if I were you I'd think twice before defining namespaces.
Update:
pseudo-superclass 1:
package proxy
{
public class Simple1
{
public var x:int;
public var y:int;
}
}
pseudo-superclass 2:
package proxy
{
import mx.controls.Alert;
public class Simple2
{
public var name:String = 'noname';
public function doAlert():void{
Alert.show(name);
}
//not normal method to replace 'this' with proxy
Simple2.prototype.doCrossClass = function doCrossClass():void{
Alert.show(''+(Number(this['x'])+Number(this['y'])));
}
}
}
Code for testing the result (looks as what you are expecting?):
var mega:Mega = new Mega();
mega.x = 100;
mega.y = 200;
mega.name = 'Multiple inheritance';
mega.doAlert();
mega.doCrossClass(); //300
And now pseudo-subclass with multiple inheritance:
package proxy
{
import flash.utils.Proxy;
import flash.utils.flash_proxy;
public dynamic class Mega extends Proxy
{
public function Mega()
{
super();
}
public var superArray:Array = [new Simple1(), new Simple2()];
flash_proxy override function getProperty(name:*):*{
for each(var superClass:Object in superArray){
if( name in superClass){
return superClass[name];
}
}
throw new Error('no such property');
}
flash_proxy override function setProperty(name:*, value:*):void{
for each(var superClass:Object in superArray){
if( name in superClass){
superClass[name] = value;
return;
}
}
throw new Error('no such property');
}
flash_proxy override function callProperty(name:*, ...args):*{
for each(var superClass:Object in superArray){
if( name in superClass){
var f:Function = superClass[name] as Function;
return f.apply(this, args);
}
}
throw new Error('no such function');
}
}
}
You can also want to use javascript-like class construction(i.e. just using simple Object and assigning properties and functions to it in any combinations you want).

Resources