javascript - Typescript generic class that implements a generic type - Stack Overflow

Is there a way to turn the following piece of code into generic typescript code? I want to have a class

Is there a way to turn the following piece of code into generic typescript code? I want to have a class that implements some interface A and that takes two elements of type A and returns different values for the properties of interface A depending on some boolean flag. I looked at typescript generics and custom types but couldn't figure it out.

Essentially, I want an element to return different values in portrait and landscape without having to resort to things like mySprite.setPosition(new Vector(10,10)) each time the orientation of my application changes. I am asking this out of curiosity, I am not looking for other solutions to this situation, I know there are many more.

Could I define some generic type, say Variant such that for any property of type A my sprite has, I could do something like mySprite.myPropOfTypeA=new Variant<A>(new A("landscape value"), new A("portrait value")) so that mySprite.myPropOfTypeA.value returns different values depending on the oute of isLandscape()? I want this to be generic so that I don't have to create a derived class for each property I want to behave like this. Thanks!

interface IVector{
    x:number,
    y:number;
}

class Vector implements IVector{
    private _x:number;
    private _y:number;

    public get x() {return this._x;}
    public get y() {return this._y;}

    constructor(x:number, y:number){
        this._x=x;
        this._y=y;
    }
}

class VariableVector implements IVector{

    private _landscape: IVector;
    private _portrait: IVector;

    constructor(landscape: IVector, portrait: IVector){
        this._landscape=landscape;
        this._portrait=portrait;
    }

    public get x() {return isLandscape()? this._landscape.x:this._portrait.x;}
    public get y() {return isLandscape()? this._landscape.y:this._portrait.y;}

}

let mySprite=new Sprite(new VariableVector(new Vector(0,0), new Vector(10,10)));

Thanks!

Is there a way to turn the following piece of code into generic typescript code? I want to have a class that implements some interface A and that takes two elements of type A and returns different values for the properties of interface A depending on some boolean flag. I looked at typescript generics and custom types but couldn't figure it out.

Essentially, I want an element to return different values in portrait and landscape without having to resort to things like mySprite.setPosition(new Vector(10,10)) each time the orientation of my application changes. I am asking this out of curiosity, I am not looking for other solutions to this situation, I know there are many more.

Could I define some generic type, say Variant such that for any property of type A my sprite has, I could do something like mySprite.myPropOfTypeA=new Variant<A>(new A("landscape value"), new A("portrait value")) so that mySprite.myPropOfTypeA.value returns different values depending on the oute of isLandscape()? I want this to be generic so that I don't have to create a derived class for each property I want to behave like this. Thanks!

interface IVector{
    x:number,
    y:number;
}

class Vector implements IVector{
    private _x:number;
    private _y:number;

    public get x() {return this._x;}
    public get y() {return this._y;}

    constructor(x:number, y:number){
        this._x=x;
        this._y=y;
    }
}

class VariableVector implements IVector{

    private _landscape: IVector;
    private _portrait: IVector;

    constructor(landscape: IVector, portrait: IVector){
        this._landscape=landscape;
        this._portrait=portrait;
    }

    public get x() {return isLandscape()? this._landscape.x:this._portrait.x;}
    public get y() {return isLandscape()? this._landscape.y:this._portrait.y;}

}

let mySprite=new Sprite(new VariableVector(new Vector(0,0), new Vector(10,10)));

Thanks!

Share Improve this question asked Aug 23, 2018 at 23:44 user1354784user1354784 4166 silver badges16 bronze badges 2
  • You're okay with reading mySprite.myPropOfTypeA.value as opposed to mySprite.propOfTypeA? – jcalz Commented Aug 24, 2018 at 0:34
  • I probably should not have used value as name choice, but value in this case would be a property in the interface A something like interface A {value: number}. A better example would be mySprite.position.x where mySprite.position is of type IPosition with interface IPosition {x:number, y:number;} and I would do mySprite.position=new Variant<IPosition>(pos1, pos2), pos1 and pos2 being any object implementing the IPosition interface. – user1354784 Commented Aug 24, 2018 at 0:36
Add a ment  | 

2 Answers 2

Reset to default 3

The ability to programmatically alter all property reads on A in the same way isn't really something a class gives you (especially since there's no way to extend a generic type... class Variant<A> extends A is invalid). What you really want to use in this case is a Proxy. At the risk of giving you an answer you don't want (not sure if this violates "I am not looking for other solutions to this situation"), here's one way you could make such Proxy objects:

function makeVariant<A extends object>(
  condition: () => boolean, 
  trueValue: A, 
  falseValue: A
): A {
  return new Proxy(trueValue, {
    get(target, prop: keyof A, receiver) {
      return condition() ? trueValue[prop] : falseValue[prop];
    }
  });
}

The function makeVariant accepts a callback and two values of generic type A, and returns an object whose properties will be drawn from one or the other value depending on whether the callback returns true or false. I'm not sure if you want the scope of the callback function to be outside of the definition of makeVariant so it doesn't need to be passed in as a parameter, but you could probably fiddle with that if you need to.

Let's see if it works:

// not sure if isLandscape() is meant to be global    
let landscape: boolean = true;
function isLandscape() {
  return landscape;
}

const variantVector = makeVariant(isLandscape, new Vector(1, 2), new Vector(3, 4));

landscape = true;
console.log(variantVector.x, variantVector.y) // 1, 2
landscape = false;
console.log(variantVector.x, variantVector.y) // 3, 4

Looks reasonable to me. Hope that points you in a useful direction. Good luck!

I guess I understand you correctly :)

class IPosition {
  constructor(public x: number, public y: number, public value: string) {}
}

class Variant<T extends IPosition> {
  constructor(private _landscape: T, private _portrait: T) {}

  public get x() { return isLandscape() ? this._landscape.x: this._portrait.x; }
  public get y() { return isLandscape()? this._landscape.y:this._portrait.y; }
}

const myPropOfTypeA = new Variant<IPosition>(new IPosition(0, 0, "landscape value"), new IPosition(10, 10, "portrait value"));

Feel free to ask questions in ments, will try to change the example in order to fit your task.

Also created a stackblitz example

发布者:admin,转转请注明出处:http://www.yc00.com/questions/1745270971a4619742.html

相关推荐

发表回复

评论列表(0条)

  • 暂无评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信