I have an abstract class defined statically and an implementation of it retrieved dynamically
Ie
export abstract class Foo {
abstract get();
}
const dynamicClass: typeof Foo = ( function() {
return class Bar {
get: function() {
console.log('get');
}
constructor() {
super();
console.log('cons');
}
}
}();
This is working fine exept one thing : I cannot call the constructor without "cheating"
IE
new Bar() output cannot instantiate abstract class
I have resolved that by doing
// #ts-ignore
new Bar();
But I feel i could do better.
The whole usecase for that is that the funciton that create the class at runtime will act differently based on the system it is (dynamiccally loading extra libraries that i removed for the sake of simplicity)
The easiest thing you can do is to not use an explicit type annotation, let the compiler infer what it will for dynamicClass:
export abstract class Foo {
abstract get(): void;
}
const dynamicClass = (function() {
return class Bar extends Foo {
get() {
console.log('get');
}
constructor() {
super();
console.log('cons');
}
}
})();
new dynamicClass();
Playground Link
If you want to go the explicit route you can use a constructor signature that returns Foo, this should mostly remove the abstarctness of the constructor:
export abstract class Foo {
abstract get(): void;
}
const dynamicClass: new () => Foo = (function() {
return class Bar extends Foo {
get() {
console.log('get');
}
constructor() {
super();
console.log('cons');
}
}
})();
new dynamicClass();
Playground Link
Related
I have created an abstract wrapper class around Electron's native BrowserWindow.
It defines two abstract accessors for the BrowserWindow. But whenever I inherit from BaseWindow, the window turns out to be undefined (note comments)!
import { BrowserWindow, BrowserWindowConstructorOptions } from "electron";
abstract class BaseWindow {
public constructor(options?: BrowserWindowConstructorOptions) {
this.setWindow(new BrowserWindow(options));
console.log("From Base", this.getWindow()); // > From Base BrowserWindow { ... }
}
public abstract getWindow(): BrowserWindow;
protected abstract setWindow(newWindow: BrowserWindow): void;
}
class MainWindow extends BaseWindow{
private window!: BrowserWindow;
public override getWindow(): BrowserWindow {
return this.window;
}
protected override setWindow(newWindow: BrowserWindow): void {
this.window = newWindow;
}
public constructor() {
super({
//...
});
console.log("From Main", this.getWindow()); // > From Main undefined
}
}
// Inside ready event
const window = new MainWindow();
console.log("From App", window.getWindow()) // > From App undefined
Am I doing something wrong? Are there any ways of fixing it?
I've been implemented a code, written by https://github.com/krazibit
import { Type } from '#nestjs/common'
import { InjectRepository } from '#nestjs/typeorm'
import * as DataLoader from 'dataloader'
import { keyBy } from 'lodash'
import { Repository } from 'typeorm'
export interface IDataService<T> {
readonly repository: Repository<T>
load: (id: string | number) => Promise<T>
loadMany: (ids: Array<string | number>) => Promise<T[]>
}
type Constructor<I> = new (...args: any[]) => I // Main Point
export function DataService<T>(entity: Constructor<T>): Type<IDataService<T>> {
class DataServiceHost implements IDataService<T> {
#InjectRepository(entity) public readonly repository: Repository<T>
private get primaryColumnName(): string {
return this.repository.metadata.primaryColumns[0]?.propertyName
}
private loader: DataLoader<number | string, T> = new DataLoader(async ids => {
const entities = await this.repository.findByIds(ids as any[])
const entitiesKeyed = keyBy(entities, this.primaryColumnName)
return ids.map(id => entitiesKeyed[id])
})
public async load(id: string | number): Promise<T> {
return this.loader.load(id)
}
public async loadMany(ids: Array<string | number>): Promise<T[]> {
return this.loadMany(ids)
}
}
return DataServiceHost
}
It should be a generic function to extend to services.
But, I'm trying to implement it using:
export class MyService extends IDataService(MyEntity)
But now, I'm trying to use the methods load and loadMany but every time I try to do it, I get an error.
Does someone knows how to do reference to those methods to execute them?
You need to inject repository inside constructor, so:
export function DataService<T>(entity: Constructor<T>): Type<IDataService<T>> {
class DataServiceHost implements IDataService<T> {
constructor(#InjectRepository(entity) public readonly repository: Repository<T>) {}
// ...
}
}
I'm instantiating the service class inside the controller class, and the log method of the service is been used in the controller.
In spec file, I'm adding spy on the log method but the spy is not been called.
Here is my code
test.service.ts
export class TestService {
public log(msg: string): void {
console.log(msg);
}
}
test.controller.ts
import { TestService } from "../service/cart.service";
export class CartController {
private testService: TestService;
constructor() {
this.testService = new TestService();
}
public testFx():void {
this.testService.log("Here is a dummy msg.")
}
}
test.controller.spec.ts
import { TestController } from "./test.controller";
import { TestService } from "./test.service";
describe("Testing controller", () => {
private testController: TestController = new TestController();
private testService: TestService = new TestService();
it ("test function", () => {
spyOn(testService, "log");
testController.testFx();
expect(testService.log).toHaveBeenCalled();
});
})
Error: - Expected spy log to have been called.
Instead of creating a new class instance,
private testController: TestController = new TestController();
private testService: TestService = new TestService();
it ("test function", () => {
spyOn(testService, "log");
you can use escape hatch for it.
Try this:
private testController: TestController = new TestController();
it ("test function", () => {
spyOn(testController["testService"], "log");
Since the private, protected and public are the concept of typescript's syntactic sugar it has nothing to do when code compiles to javascript.
The more explaining answer is here.
Some time ago I asked how to make a class implement an interface that it doesn't implement by declaration.
One possibility is as-coercion:
interface MyInterface {
def foo()
}
class MyClass {
def foo() { "foo" }
}
def bar() {
return new MyClass() as MyInterface
}
MyInterface mi = bar()
assert mi.foo() == "foo"
Now that I try to use it, I don't know what interface is needed at compile-time. I tried using generics like this:
interface MyInterface {
def foo()
}
class MyClass {
def foo() { "foo" }
}
class Bar {
public static <T> T bar(Class<T> type) {
return new MyClass() as T
}
}
MyInterface mi = Bar.bar(MyInterface.class)
assert mi.foo() == "foo"
But it raises the following exception:
Cannot cast object 'MyClass#5c4a3e60' with class 'MyClass' to class 'MyInterface'
How can I coerce to an interface that is only known at run-time?
interface MyInterface {
def foo()
}
interface AnotherInterface {
def foo()
}
class MyClass {
def foo() { "foo" }
}
class Bar {
static <T> T bar(Class<T> type) {
new MyClass().asType(type)
}
}
MyInterface mi = Bar.bar(MyInterface)
assert mi.foo() == "foo"
AnotherInterface ai = Bar.bar(AnotherInterface)
assert ai.foo() == "foo"
I want a ArrayList, where you can add Objects , which implements an Interface.
Something like this:
ArrayList<Object implements Interface> list =
new ArrayList<Object which implements a specific Interface>();
What is the correct syntax for this?
Just set the interface as the type of the generic. Here you go the code in C#:
interface IFoo {
void foo();
}
class Foo1 : IFoo {
public void foo() {
Console.WriteLine("foo1");
}
}
class Foo2 : IFoo {
public void foo() {
Console.WriteLine("foo2");
}
}
class Program {
public static void Main() {
// IFoo type: any object implementing IFoo may go in
List<IFoo> list = new List<IFoo>();
list.Add(new Foo1());
list.Add(new Foo2());
foreach(IFoo obj in list) obj.foo(); // foo1, foo2
Console.ReadKey();
}
}