javascript - Typescript generisk klass som implementerar en generisk typ


Finns det ett sätt att göra följande kod till generisk TypeScript-kod? Jag vill ha en klass som implementerar ett gränssnitt A och som tar två element av typen A och returnerar olika värden för egenskaperna i gränssnittet A beroende på någon boolesk flagga. Jag tittade på TypeScript-generics och anpassade typer, men kunde inte lista ut det.

I princip vill jag att ett element ska returnera olika värden i porträtt och landskap utan att behöva använda saker som mySprite.setPosition(new Vector(10,10)) varje gång orienteringen av min applikation ändras. Jag frågar detta av nyfikenhet, jag letar inte efter andra lösningar på denna situation, jag vet att det finns många fler.

Skulle jag kunna definiera någon generisk typ, säg Variant så att för vilken egenskap av typen A som min sprite har, skulle jag kunna göra något som mySprite.myPropOfTypeA=new Variant<A>(new A("landscape value"), new A("portrait value")) så att mySprite.myPropOfTypeA.value returnerar olika värden beroende på resultatet av isLandscape()? Jag vill att detta ska vara generiskt så att jag inte behöver skapa en härledd klass för varje egenskap jag vill ska bete sig på detta sätt. Tack!

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)));

Tack!

Svar #1

Möjligheten att programmatiskt ändra alla egenskapsavläsningar på A på samma sätt är egentligen inte något en klass ger dig (särskilt eftersom det inte finns något sätt att utöka en generisk typ... class Variant<A> extends A är ogiltigt). Vad du egentligen vill använda i det här fallet är en Proxy. På risken att ge dig ett svar du inte vill ha (osäker på om detta bryter mot "Jag letar inte efter andra lösningar på denna situation"), här är ett sätt du skulle kunna skapa sådana Proxy-objekt:

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];
    }
  });
}

Funktionen makeVariant accepterar en callback och två värden av den generiska typen A, och returnerar ett objekt vars egenskaper kommer att väljas från det ena eller det andra värdet beroende på om callbacken returnerar true eller false. Jag är inte säker på om du vill att räckvidden för callback-funktionen ska vara utanför definitionen av makeVariant så att den inte behöver skickas in som en parameter, men du kan förmodligen justera det om du behöver.

Låt oss se om det fungerar:

// 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

Ser rimligt ut för mig. Hoppas det pekar dig i en användbar riktning. Lycka till!

Svar #2

Jag antar att jag förstår dig korrekt :)

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"));

Känn dig fri att ställa frågor i kommentarerna, jag kommer att försöka ändra exemplet för att passa din uppgift.

Skapade också ett stackblitz-exempel

Relaterade artiklar