Let's say I have two concrete factories:
class concreteFactory1 = {
inherit abstractFactory;
pub createProductA => (new productA1);
pub createProductB => (new productB1);
};
class concreteFactory2 = {
inherit abstractFactory;
pub createProductA => (new productA2);
pub createProductB => (new productB2);
};
I then would like to have another class that calls factory method(e.g. concreteFactory1#createProductA), based on factory passed into class. Something like the following (syntax is wrong, I know):
class testFactory factory => {
as _;
pub createProductA => Js.log factory#createProductA;
};
Any suggestions are more than welcome. Thank you.
Related
I want to have a common fixture for my tests:
#RunWith(JUnitPlatform::class)
abstract class BaseSpek: Spek({
beforeGroup {println("before")}
afterGroup {println("after")}
})
and now I want to use that spec:
class MySpek: BaseSpek({
it("should xxx") {}
})
but i got compilation error due to no-arg BaseSpek constructor. what's the correct way of achieving what i need?
You can define an extension on Spec that sets up the desired fixture and then apply it in your Speks as follows:
fun Spec.setUpFixture() {
beforeEachTest { println("before") }
afterEachTest { println("after") }
}
#RunWith(JUnitPlatform::class)
class MySpek : Spek({
setUpFixture()
it("should xxx") { println("xxx") }
})
Though this is not exactly what you asked, it still allows flexible code reuse.
UPD: This is a working option with Speks inheritance:
open class BaseSpek(spec: Spec.() -> Unit) : Spek({
beforeEachTest { println("before") }
afterEachTest { println("after") }
spec()
})
#RunWith(JUnitPlatform::class)
class MySpek : BaseSpek({
it("should xxx") { println("xxx") }
})
Basically, do do this, you invert the inheritance direction, so that the child MySpek passes its setup in the form of Spec.() -> Unit to the parent BaseSpek, which adds the setup to what it passes to Spek.
I have a testFactory class. The purpose is to be able to pass in a factory, and then console out results for demoing purposes. As of now, when trying to call createProductA within the test method , the compiler will complain that createProductA is unbound (Unbound value createProductA).
What is the proper syntax for calling a method internally in a class?
class testFactory (factory: abstractFactory) => {
as _;
pub createProductA => factory#createProductA;
pub createProductB => factory#createProductB;
pub test () => {
Js.log createProductA;
Js.log createProductB;
}
};
This is where the as _; part of the class definition comes in, if you've ever wondered what that was for.
createProductA and createProductB are methods, not functions, so they need to be called on an object. Reason/OCaml won't automatically bind the current object to a name like this or self, but puts it on you to do it, which is precisely what as does, and _ means, as usual, "I don't care about this". So if you change as _; to e.g. as self; you'll be able to reference self as the current object elsewhere.
Try this:
class testFactory (factory: abstractFactory) => {
as self;
pub createProductA => factory#createProductA;
pub createProductB => factory#createProductB;
pub test () => {
Js.log self#createProductA;
Js.log self#createProductB;
}
};
I have two abstract products types:
type abstractProductA = {.
methodA: string
};
type abstractProductB = {.
methodB: int
};
Used to create the following product classes:
class productA1 = {
pub methodA => "This is methodA of ProductA1";
};
class productB1 = {
pub methodB => 1;
};
I would like to call the instance of abstractProductA, as well as abstractProductB in my abstract factory. Something like the following(syntax is off, I know):
type abstractFactory = {.
createProductA: abstractProductA,
createProductB: abstractProductB
};
So that when I create new concreteFactory using the following class:
class concreteFactory1 = {
pub createProductA => (new productA1);
pub createProductB => (new productA1);
};
and constructer:
let g = new concreteFactory1#createProductB;
Js.log (g#methodA);
the compiler should complain that createProductB only takes an int, and not an string(which it currently does not).
Thank you, and any suggestions are more than welcome.
It seems the error should occur where createProductB returns productA1 instead of productB1. In order to achieve that, you'll need to define a virtual class for abstractFactory instead of just an object type, and then have concreteFactory explicitly inherit from it.
class virtual abstractFactory = {
pub virtual createProductA: abstractProductA;
pub virtual createProductB: abstractProductB;
};
class concreteFactory1 = {
inherit abstractFactory;
pub createProductA => (new productB1);
pub createProductB => (new productB1);
};
This will produce the following error on pub createProductA => (new productB1):
This expression has type productB1 but an expression was expected of type abstractProductA The second object type has no method methodB
See the full example here
Let's say I have the following abstractProductA class with a public method called methodA :
class abstractProductA = {
pub methodA => "name";
};
I would like to create an interface that says function methodA should always return a string. Something similar to
interface abstractProductA {
abstractProductA(): string
}
only in reason, and then have class implement it. Any suggestions are more than welcome. Thank you
What you're really asking for it seems is how to define and use an abstract class, which is called a virtual class in OCaml/Reason:
class virtual virtualProductA = {
pub virtual methodA: string;
};
class abstractProductA = {
inherit virtualProductA;
pub methodA = "name";
};
An interface is more for consumers to abstract away an implementation, and while a virtual class can be used as an interface by itself, since OCaml/Reason objects are structurally typed you can also just specify the object type you need. And of course you can bind it to a name if you like:
type interfaceA = {.
methodA : string
};
let f (p: interfaceA) => Js.log p#methodA;
f (new abstractProductA);
I am using a custom library, and for some types I have to write:
import * as pipeline from 'custom-pipeline';
import {MapTransform} from 'custom-pipeline';
export const createTransform = (appConfig: IAppConfig):
MapTransform<ISomeData, IFoo> |
MapTransform<ISomeData, IBar> => {
switch(appConfig.createType) {
case 'foo':
return new pipeline.MapTransform<ISomeData, IFoo>((data: ISomeData) => {...});
case 'bar':
return new pipeline.MapTransform<ISomeData, IBar>((data: ISomeData) => {...});
}
}
Especially the verbose constructor is irking me. I can I alias the types alright:
type FooTransform = MapTransform<ISomeData, IFoo>;
type BarTransform = MapTransform<ISomeData, IBar>;
Yet I cannot do:
new FooTransform((data: ISomeData) => {...});
new BarTransform((data: ISomeData) => {...});
throwing an error like:
error TS2304: Cannot find name 'FooTransform'.
I assume it's because I only have a type and not a class? Yet how can I alias the constructor in a way that I can do new FooTransform as above?
The definition of MapTransform looks like:
export declare class MapTransform<A, B> extends BaseTransform<A, B> {
constructor(private mapFunc: (val: A) => B);
}
I can reduce the constructor to this:
fooMapFunction = (data: ISomeData): IFoo => {...};
new MapTransform<ISomeData, IFoo>(mapFunction);
Albeit it's not en par with a new FooTransform(fooMapFunction).
Your assumption is correct, type declarations are compile-time only, thus you can't perform any operations on them (including instantiation with new).
Solution 1: Type Assertion
Let's assume the superclass looks like this:
class MapTransform<T1> {
constructor(public readonly data: T1) { /* ... */ }
}
To create a specialised type alias, you can do this:
type TransformA = MapTransform<number>;
From the perspective of the superclass MapTransform, there's no difference between MapTransform<A>, MapTransform<B> and so on (that's the point of generics), so we can safely assign the constructor function of class MapTransform to a constant TransformA. Calling new TransformA() is, at runtime, identical to calling new MapTransform<number>():
const TransformA = <{ new (data: number): TransformA; }>MapTransform;
Notice the type assertion? This tells the TypeScript compiler to treat the assigned value MapTransform as an object which constructs an object of type TransformA when instantiating it with new. We can now write:
const a = new TransformA(123);
a.data.toExponential(); // works
BTW, what the TypeScript compiler actually sees is this ...
const TransformA = <{ new (data: number): MapTransform<number>; }>MapTransform;
... because type TransformA ≡ MapTransform<number>.
Be aware that all of these will evaluate true:
new TransformA(123) instanceof TransformA
new TransformA(123) instanceof MapTransform
new MapTransform<number>(123) instanceof TransformA
new MapTransform<number>(123) instanceof MapTransform
Here's the example in the TypeScript Playground.
Solution 2: Subclassing
Again, let's assume the superclass looks like this:
class MapTransform<T1> {
constructor(public readonly data: T1) { /* ... */ }
}
With subclassing, the solution is simple: Extend the superclass and pass the desired type parameters along in the extends clause.
class TransformA extends MapTransform<number> { }
Et voilà, you now have a constructor that works at runtime as well as a type that works at compile-time.
Unlike the first solution, the following 2 expressions will evaluate true ...
new TransformA(123) instanceof TransformA
new TransformA(123) instanceof MapTransform
... while these will evaluate false:
new MapTransform<number>(123) instanceof TransformA
new MapTransform<number>(123) instanceof MapTransform
Solution 2.1: Anonymous Subclassing
If you only need the constructor alias but not the type alias, this might come in handy:
const TransformA = class extends MapTransform<number> { };
This is called a class expression and can be used like every other expression, for example:
class App {
private static TransformA = class extends MapTransform<number> { };
public doStuff() {
return new App.TransformA(123);
}
}
More on Types
If you're interested, here's a few more links on the topic:
Named Types in TypeScript Spec
Type Assertions
TypeScript Handbook: Interfaces, specifically section "Difference between the static and instance sides of classes"
Class Expressions
EDIT
You wrote you've had issues applying these solutions to a class that expects a function as a parameter, so here's another example in the TypeScript playground.