Anonymous extended class typedef - javascript

I got this working function which I would like to have a better typedef for than any as action and return type. action should be a class and the return type should be class which extends action.
function createAction(state: string, action: any): any {
return class extends action {
public static readonly type = `[${state}] ${action.name}`;
constructor(...args: any) {
super(...args);
}
};
}
I tried using Angulars Type<T> which didn't work out.
If the only option to make this work would be to have an Interface on my action class and using that Interface for typedef it would be fine as well, as long as the Interface would be somewhat generic.
I imagine something along the lines of this as a solution, but i can't quite get it to work:
function createAction<T>(state: string, action: Type<T>): Type<X extends T> {

Related

Can you use super class methods as decorator methods - Typescript

This is something that if I'm able to achieve will be able to design a very easily extendible decorator factory for a project I'm working on.
Basically, I want to be able to use a super class's methods as a decorator for the sub-classes property.
This is usually how decorators work:
import { handleSavePropertyNameDecorator } from "./W-ODecorator"
class Main {
#handleSavePropertyNameDecorator
test1: string = ""
}
export default Main
And then the decorator function:
const propertyNames = [];
export const handleSavePropertyNameDecorator = (_instance: any, propertyName: string) => {
console.log("Executing handleSavePropertyNameDecorator")
propertyNames.push(propertyName);
}
However, instead of defining a separate function for the decorator, I'd like the decorator function to come from the super class:
import SuperClass from "./SuperClass"
class Main extends SuperClass{
#this.handleDecoratorFunction
test1: string = ""
}
export default Main
class SuperClass {
static propertyNameArray: string[] = [];
protected handleDecoratorFunction(_instance: any, propertyName: string) {
console.log("executing handle decorator function from super class!")
SuperClass.propertyNameArray.push(propertyName);
}
}
export default SuperClass;
Currently the keyword "this" has a compilation error that states it's possibly undefined. I've run this code and it doesn't execute the decorator function.
My question is this approach possible with some type of workaround? If this is possible that will significantly improve my organization.
Thank you in advance!
No, you can't do that, the decorator is applied when the class is defined not when it's instantiated. There are no instance methods from your superclass available.
However, you can do this with a static method of course:
class SuperClass {
static propertyNameArray: string[] = [];
protected static handleDecoratorFunction(_instance: any, propertyName: string) {
console.log("executing handle decorator function from super class!")
this.propertyNameArray.push(propertyName);
}
}
class Main extends SuperClass{
#SuperClass.handleDecoratorFunction
test1: string = ""
}
export default Main

Strict typing issue with generic types in typescript

I am trying to learn strict typing in Typescript.
I defined these classes:
export abstract class MyAbstractClass<TParam extends MyParamBaseType> {
private param: TParam;
setInitParams(init: TParam): void {
...
}
getInitParams(): TParam {
....
}
}
export class MyClass extends MyAbstractClass<AParamType> {
private param: AParamType;
...
}
The problem is that I get the error " Class 'MyClass' incorrectly extends base class 'MyAbstractClass'.
Types have separate declarations of a private property 'param'."
I don't understand why I get this error because AParamType type correctly extends MyParamBaseType
Can somebody helps me ? Thanks for your help.
The private keyword is just a compile time checked. This means that the field param will be stored at runtime in the instance of the class. MyAbstractClass declares it's member private, so if MyClass were allowed to redeclare the param field it would end up accessing the same field named param in the instance at runtime breaking privacy.
You can use the ES private class fields (with #). These ensure hard privacy even at runtime and name collisions in the sub class are not an issue since each declared field is distinct even if they share the same name:
type MyParamBaseType = {}
export abstract class MyAbstractClass<TParam extends MyParamBaseType> {
#param!: TParam;
setInitParams(init: TParam): void {
}
getInitParams(): TParam {
return this.#param;
}
}
type AParamType = {}
export class MyClass extends MyAbstractClass<AParamType> {
#param!: AParamType;
}
Playground Link
Or if you want to access the same field from the base class you might consider protected instead:
type MyParamBaseType = {}
export abstract class MyAbstractClass<TParam extends MyParamBaseType> {
protected param!: TParam;
setInitParams(init: TParam): void {
}
getInitParams(): TParam {
return this.param
}
}
type AParamType = {}
export class MyClass extends MyAbstractClass<AParamType> {
protected param!: AParamType;
}
Playground Link

typescript type of dynamic class methods

i am trying to build a class that build some dynamic methods on constructor stage, everything works well, but VS Code auto suggestion does not work for dynamic methods? how should we do that?
here is the codeSandBox
i have also tried interface but still no luck
export default class A {
private version = 1;
get getVersion() {
return this.version;
}
private actions = ["approve", "edit", "delete"];
constructor() {
this.actions.forEach(
method =>
(A.prototype[method] = route => {
console.warn(method + " called");
})
);
}
}
const B = new A();
console.warn(B.getVersion);
console.warn(B.approve()) // auto suggestion not available here
You can do this... but it is really rather hacky. Meta programming like this is impossible to fully type check. Here's a playground with this solution.
First, tell TS that actions is a constant array so that it can narrow the type.
private actions = ["approve", "edit", "delete"] as const;
Next, create a mapped type that describes what your forEach method adds to the type.
type _A = {
[K in A['actions'][number]]: (route: string) => void
}
Note that this has to be a type. It can't be an interface.
Finally, add an interface that extends this type alias. I expected TS to yell at me here about circularity, but apparently this is OK.
export default interface A extends _A {
}

Trying to override and extend method signature in child class in Typescript

I have a base class that I am trying to extend:
export class BaseClass<T extends SomeOtherClass> {
constructor(param: ParamType) {
}
doSomething(param1: Param1Type): BaseClass<T> {
// do something with param1;
return this;
}
}
My class:
export class MyClass<T extends SomeOtherClass> extends BaseClass<T> {
constructor(param: ParamType) {
super(param);
}
doSomething(param1: Param1Type, param2: Param2Type): MyClass<T> {
// super.doSomething(param1);
// do something with param2;
return this;
}
}
but I'm getting a warning:
Property 'doSomething' in type 'MyClass<T>' is not assignable to the same property in base type 'BaseClass<T>'.
Type '(param1: Param1Type, param2: Param2Type) => MyClass<T>' is not assignable to type '(param1: Param1Type) => BaseClass<T>'.
Is it not possible to extend method signatures in typescript? How do I extend the capabilities of the BaseClass if I need to add a parameter to the overridden method and is this the correct way of calling the parent method in es6 syntax. I'm aware that prior to es6 I could have called BaseClass.prototype.doSomething.call(this, param1).
The problem as pointed out by others is that if param2 is required it breaks polymorphism:
// We should be able to do this assignment
let baseRef: BaseClass<SomeOtherClass> = new MyClass<SomeOtherClass>("");
baseRef.doSomething("") // param2 is not required by the base class so MyClass will not receive it even though it NEEDS it
One solution, is to make the second parameter optional, so the call baseRef.doSomething("") is valid for the derived type as well :
export class MyClass<T extends SomeOtherClass> extends BaseClass<T> {
constructor(param: string) {
super(param);
}
doSomething(param1: string, param2?: string): MyClass<T> {
super.doSomething(param1);
return this;
}
}
A second solution, if we only want to share code between the classes, is to disallow the assignment let baseRef: BaseClass<SomeOtherClass> = new MyClass<SomeOtherClass>(""); by not really inheriting BaseClass but rather inherit a class that excludes the doSomething method:
type PartialBaseClass = new <T> (param: string) => { [P in Exclude<keyof BaseClass<T>, 'doSomething'>] : BaseClass<T>[P] }
const PartialBaseClass:PartialBaseClass = BaseClass
export class MyClass<T extends SomeOtherClass> extends PartialBaseClass<T> {
constructor(param: string) {
super(param);
}
doSomething(param1: string, param2: string): MyClass<T> {
BaseClass.prototype.doSomething.call(this, param1);
return this;
}
}
// This is now invalid !
let baseRef: BaseClass<SomeOtherClass> = new MyClass<SomeOtherClass>("") ;
This is a violation of OOP as it would break polymorphism. An example where you might typically use this could be with a base class Filter and a derived class FilterWithDelay. The parent class implements a method called setFilter. In OOP you might have an array of Filters which contains instances of both Filter and FilterWithDelay. You might typically want to iterate through the array and call setFilter on each object. For this to be possible the child method should implement the same function signature as the parent method. Otherwise the code, would need to check each instance to see if its a Filter or FilterWithDelay in order to pass in the additional parameters.
In order to implement the FilterWithDelay you can extend the parent class and pass the delay as a parameter to the constructor instead of the method. This way setFilter can implement the common interface.
export class BaseClass<T extends SomeOtherClass> {
constructor(param: ParamType) {
}
doSomething(param1: Param1Type): BaseClass<T> {
// do something with param1;
return this;
}
}
export class MyClass<T extends SomeOtherClass> extends BaseClass<T> {
constructor(param: ParamType, param2: Param2Type) {
super(param);
this.param2 = param2;
}
doSomething(param1: Param1Type): MyClass<T> {
return super.doSomething(param1 + this.param2);
}
}

TypeScript: anonymouse class factory

My TypeScript v2.2.
I have this class factory:
export class A { name: string; }
export function makeConstructor(name: string)
{
const newClass = class extends A { };
newClass.prototype.name = name;
return newClass;
}
TypeScript throw error:
Return type of exported function has or is using private name '(Anonymous class)'.
I can say that this factory returns any to hide error, but how I can explain what exactly returns?
I tried to write
makeConstructor<T extends A>(name: string): T
makeConstructor<T extends typeof A>(name: string): T
makeConstructor<T extends A['prototype']>(name: string): T['prototype']
When you use the Factory design pattern, you should probably want to shield the exact implementation class chosen by the Factory, and just return A (or its interface) instead. Therefore, I think returning A just does the trick, no need for generics etc...

Categories