Class as function parameter in Typescript - javascript

In Angular 2 testing utility I do something like this:
fixture = TestBed.createComponent(EditableValueComponent);
where EditableValueComponent is a normal component class.
I wonder how it works:
static createComponent<T>(component: Type<T>): ComponentFixture<T>;
Beceause I wanna do something similar (I want to simplify some testing stuff):
export class SuperFixture<T>
{
fixture: ComponentFixture<T>;
component: T;
constructor()
{
this.fixture = TestBed.createComponent(T); // <--- problem here!
this.component = this.fixture.componentInstance;
}
}
The problem is:
'T' only refers to a type, but is being used as a value here.'
EDIT #1
I solved the problem this way:
constructor(component)
{
this.fixture = TestBed.createComponent<T>(component);
But I still don't know how it works..

You still need to pass actual class (constructor function that creates instances of a class) into constructor function of SuperFixture. Under the hood TestBed.createComponent calls provided constructor function with new to create instance of provided class. So SuperClass signature might look like this:
class SuperFixture<T>
{
fixture: ComponentFixture<T>;
component: T;
// passing in the constructor for instances of T
constructor(componentConstructor: new () => T)
{
this.fixture = TestBed.createComponent<T>(componentConstructor);
this.component = this.fixture.componentInstance;
}
}

Was working on that answer, but had to step out for coffee. ¯_(ツ)_/¯
The language feature you are using is called a Generic in TypeScript. It allows defining types at runtime with "type variables" (like <T>), which are separate from function arguments.
Before, the type variable was being passed as a function argument, when the function expected an instance of type T. That's what the error means.
The change you made works because you are passing the type variable and the instance in their correct positions in the call.
The SuperFixture object gets the value of T when you create it, and then it will pass that type variable to createComponent in the constructor, along with the value of component.

Related

Using variables from create in the update function Phaser 3

In my game I am trying to use one of my variables I have initiated in my create function outside of that scope and to be used in the update function. Ideally my code would look like this:
create()
{
const map = this.make.tilemap({ key: 'mainmap' })
const tileset = map.addTilesetImage('Serene_Village_16x16', 'tiles', 16, 16, 1, 2)
const Next1 = map.createLayer('Next', tileset)
update(t: number, dt: number){
this.physics.world.collide(this.faune, Next1, ()=>{
console.log("testing")
this.scene.stop(),
this.scene.start('secondmap');
});
The problem with this however is that I can not access next1 to collide with my player character "faune" since the error given is that I "Cannot find name 'Next1'.". If anyone has any idea how to use this across functions with Phaser that would be extremely helpful.
Thanks, arthur
The problem is that you are defining the function Next1 in the create function and trying to access it in the update function.
you could, simply define a property in the create function, like this:
this.next1 = map.createLayer('Next', tileset)
and in the update function, use it like this:
this.physics.world.collide(this.faune, this.next1, () => {
// ...
});
for this not to through a compiler error, when using typescript you would have to add the property to the class definition.
class myScene extends Phaser.Scene {
// you can use the exact datatype if you know it
public next1:any;
...
}
Or simply put the this.physics ... code into the create function.

Javascript Class constructor - instances, pointers, and private var references

I have a class I reuse, often, to the tune of possibly tens of thousands of instances in a given session. It occurred to me that creating all these properties within the constructor may be replication, that is each function is unique in memory and not a pointer, so I created a little test setup:
const testTree = function (){
console.log(this, this.konnichiwa);
}
const testFjord = function (aloha){
return function() {
console.log(this, aloha, this.konnichiwa);
}
}
class Clown extends Object{
constructor(props){
super(props);
const aloha = "Hello!"; //<- Private party
this.konnichiwa = "Also hello I think"; //<- Everyone's invited
this.testWan = () => {
console.log(this, aloha, this.konnichiwa);
}
this.testTree = testTree;
this.testFjord = testFjord(aloha);
}
testToo = () => {
console.log(this, this.konnichiwa);
}
}
//export default Clown; //this is an export in my application, used lots
const test = new Clown();
const otherTest = new Clown();
console.log(test.testWan === otherTest.testWan);
console.log(test.testToo === otherTest.testToo);
console.log(test.testTree === otherTest.testTree);
console.log(test.testFjord === otherTest.testFjord);
test.testWan();
test.testToo();
test.testTree();
test.testFjord();
Part 1
As you can test above, testWan, testToo, and testFjord are all unique per instance, but testTree is not. Is there any way to declare a "pointer"/"reusable function" but inside class constructor?
The issue here with testToo and testTree is that they can't access private vars within the constructor like testWan can. testFjord is a factory and can be passed these, but then the returned function is unique and won't be able to interact well with vars passed into it.
It's very likely not possible - I think it's a catch 22 scope thing - but you may know better. The only recourse I can think of is to add a property to this for each thing I need to use in testTree, but that exposes things I may not want exposed outside of the class.
Part 2
This part only applies if this is a generally consistent behavior, and not something completely unique per-browser. Does the engine hold onto references to things like conditionals (which I suspect are sorta anonymous-function-like behind the scenes) once the constructor has run?
I have a fairly knarly conditional setup I'm not going to shove in the code here. This is entirely within the constructor right now. I suspect that, although not a function declaration itself, it is also not a pointer, but an entirely fresh instance per the 'new' in new Clown. It needs to manipulate some private vars and so per Part 1 I haven't figured out a good way to extract this.
Example, there are references to private vars inside the constructor for exposed functions: aloha above is private but used by public testWan function, and so needs to be held after constructor has executed. Is the entire constructor held for the life of test & otherTest or is the constructor going to be dropped after use and just the reference to aloha held in memory?

Attach a class method to window object using decorators

I am using VueJS with typescrpit but this could be in any js framework.
I have componenets with some methods I want to expose globally so they can be used via the browser console. First idea is to attach them to the window object. This could be done from a lifecycle method such us mounted in my case, but I prefered a cleaner easier solution to write and to use, using decorators.
I've tried something like:
mycomponenet.ts :
function makeGlobal() {
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
(window as any)[propertyKey] = () => target[propertyKey]();
};
}
Then this decorator will be used easily like :
#makeGlobal()
myMethodToGoGloabl(){
// do stuff ...
}
Till now everything works fine, until the function uses "this" like :
#makeGlobal()
myMethodToGoGloabl(){
this.firstName = "";
}
Then I get a error firstName of undefined. I understood by searching and reading that the decorator is executed before class instantiation, therefor what I exposed (correct me if I m mistaken) is a prototype method and not a method of the instance that why we have no access to this. I've tried to bind(this) but I failed for the same reason that I don't expose globally the method of the instance.
Is there a way to use decorators in my case or they are useless here?
Guess the issue is your decorator. You do not take over the this instance at all...
Also the way you do it, you won't be able to pass parameters.
Suggestion:
function makeGlobal() {
return function(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
var currentThis = this;
(window as any)[propertyKey] = () => target[propertyKey].apply(currentThis, args);
};
}
Also keep in mind what Christian says in the comment. It basically can only work for singletons, as it will override the method for multiple instances

Create ES6 class from a function

I'm trying to explore using ES6 classes instead of how we do it currently, using the Function.prototype means. Currently our API looks like:
var myclass = createClass('MyClass', {
test : function() {}
});
We iterate through the object and apply those properties onto the Function that we return, basically a prettier way than to do so that it's more inline with other programming languages of sorts:
function MyClass() {}
MyClass.prototype.test = function() {};
We also cache the class onto an object where name is the key and the function is the value for use throughout our application. The class name can be namespaced so you can have My.Cls and it will split by the period and then cache it onto the manager but it also can be retrieved via window.My.Cls.
Looking into ES6 classes, I don't see how I can keep the createClass function. Would love something like:
function createClass(name, config) {
return class name config;
}
I didn't expect it to work and it doesn't.
Two issues I have here:
How can I create a class using a variable as the class name?
How can I create a class and assign the properties via the config object argument?
Not sure this would be possible. We don't plan on keeping the createClass, we hope to keep it for now and upgrade our legacy "classes". I'd like to start using ES6 classes but not break the whole app for however long it'll take us to fully upgrade.
The only good upgrade route is to refactor the property hashes into proper classes. You can start that work and keep using your hash-based classes in the meantime, which will lighten the requirement to do it all at once.
If you have a limited number of "class" name:config pairs -- which you should for maintainability reasons -- then you can replace createClass with an implementation that does:
class Foo { ... }
class Bar { ... }
let classes = {'Foo': Foo, 'Bar': Bar};
function createClass(name, config) {
if (classes[name]) {
return classes[name];
}
// old impl
}
This will ignore the config if a "real" implementation exists, but keep using the legacy behavior if you haven't replaced the class. If it is, you can implement createClass more like:
function createClass(name, config) {
if (classes[name]) {
return new classes[name](config);
}
// old impl
}
and pass the config arguments into the class ctor. In this case, you may want to filter out function properties (methods) first, as the class probably implements them already. Something like:
function createClass(name, config) {
if (classes[name]) {
let fields = Object.keys(config).filter(key => {
return typeof config[key] !== 'function';
}).map(key => config[key]);
return new classes[name](fields);
}
// old impl
}

Reference an inferred type in TypeScript

Is there any way to reference an inferred type in TypeScript?
In the following example we get nice inferred types.
function Test() {
return {hello:"world"}
}
var test = Test()
test.hello // works
test.bob // 'bob' doesn't exist on inferred type
But what if I want to define a function that takes a parameter of the type: "Whatever Test returns", without explicitly defining the interface?
function Thing(test:???) {
test.hello // works
test.bob // I want this to fail
}
This is a workaround, but it gets hairy if Test has parameters of its own.
function Thing(test = Test()) {} // thanks default parameter!
Is there some way to reference the inferred type of whatever Test returns? So I can type something as "Whatever Test returns", without making an interface?
The reason I care is because I usually use a closure/module pattern instead of classes. Typescript already lets you type something as a class, even though you can make an interface that describes that class. I want to type something as whatever a function returns instead of a class. See Closures in Typescript (Dependency Injection) for more information on why.
The BEST way to solve this is if TypeScript added the abilty to define modules that take their dependencies as parameters, or to define a module inside a closure. Then I could just use the spiffy export syntax. Anyone know if there are any plans for this?
It's now possible:
function Test() {
return { hello: "world" }
}
function Thing(test: ReturnType<typeof Test>) {
test.hello // works
test.bob // fails
}
https://www.typescriptlang.org/docs/handbook/advanced-types.html#type-inference-in-conditional-types
You can use the body of an interface as a type literal:
function Thing(test: { hello: string; }) {
test.hello // works
test.bob // I want this to fail
}
is equivalent to
interface ITest {
hello: string;
}
function Thing(test: ITest) {
test.hello // works
test.bob // I want this to fail
}
Just don't forget the ; at the end of each member.
There is no syntax for naming or referring to inferred types. The closest you can get is using interfaces or type-literals for the members you are going to use. Interfaces and type-literals will match whatever type which at least has the defined members. "Duck-typing"

Categories