• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Wnioskowanie typów - typescript

Object Storage Arubacloud
+4 głosów
249 wizyt
pytanie zadane 18 lutego 2022 w JavaScript przez rafal.budzis Szeryf (85,260 p.)
edycja 19 lutego 2022 przez rafal.budzis

Hej przyszło mi otypować zaawansowana rzecz. implementuje sobie wzorzec projektowy odwiedzającego. https://refactoring.guru/pl/design-patterns/visitor i chciałem aby odwiedzający mógł decydować o zwracanym typie. Zrobiłem mały przykład kodu aby było wiadomo o co chodzi ;) 

Chciałbym wiedzieć w jaki sposób otypować funkcje aby typescript był w stanie określić zwracany typ na podstawie typu parametru. W przykładzie dorzuciłem komentarze o co chodzi ;) 

abstract class Figure {
    abstract visit<T extends IVisitor>(visitor: T): any;
}

class Square {
    n: number;
    constructor(n:number){
        this.n = n;
    }
    visit<T extends IVisitor>(visitor: T): ReturnType<T['visitSquer']> {
        return visitor.visitSquer(this);
    }
}

class Circle {
    r: number;
    constructor(r:number){
        this.r = r;
    }
    visit<T extends IVisitor>(visitor: T): ReturnType<T['visitCircle']> {
        return visitor.visitCircle(this);
    }
}

class Rectangle {
    a: number;
    b: number;
    constructor(a:number, b:number){
        this.a = a;
        this.b = b;
    }
    visit<T extends IVisitor>(visitor: T): ReturnType<T['visitRectangle']> {
        return visitor.visitRectangle(this);
    }
}

interface IVisitor {
    visitSquer(object: Square): any;
    visitCircle(object: Circle): any;
    visitRectangle(object: Rectangle): any;
}
 
// Czy w jakiś sposób mozna wykonać wnioskowanie generyka dla interface 
// na podstawie zadeklarowanych metod visitSquer, visitCircle, visitRectangle ?
// aby nie musieć podawać ręcznie :/ 
 
class SideSizeVisitor implements IVisitor {
    visitSquer(object: Square): number {
        return object.n;
    };
    visitCircle(object: Circle): number {
        return 2 * object.r * Math.PI;
    };
    visitRectangle(object: Rectangle): [number,number] {
        return [object.a, object.b];
    };
 
    visit<T extends Figure>(object: T): ReturnType<T['visit']>{
        return object.visit<SideSizeVisitor>(this);
    }
}
 
const foo =  new SideSizeVisitor();
const square = new Square(10);
const circ = new Circle(5);
const rect = new Rectangle(4,6);
 
// No i tu mam błąd bo visit zwraca inne dana w zależności czy w parametrze mamy kwadrat czy koło.
// W jaki sposób otypować metode visit aby typescript był w stanie określić zwracany typ na podstawie typu parametru? 
const resultSquare: number = foo.visit(square);
const resultCirc: number = foo.visit(circ);
const resultRect: [number, number] = foo.visit(rect);

// W tej chwili zwraca any dlatego typ string nie pluje błedem a w tym wypadku powinien.
const resultSquare2: string = foo.visit(square);

 

1 odpowiedź

0 głosów
odpowiedź 18 lutego 2022 przez ScriptyChris Mędrzec (190,190 p.)

Hmm, ciekawy przypadek. :)

Stworzyłem nowy typ generyczny, który wyciąga typ z unii `Object[N]`:

type TObjs<T> = Extract<T, Object1 | Object2 | Object3>

, ustawiłem argument o generycznym typie dla metody method na podstawie nowo stworzonego typu:

method<T>(object: TObjs<T>) {

i usunąłem samodzielne deklarowanie typu dla zmiennej result.

const result = foo.method(obj1);

TypeScript wnioskuje wtedy typ Result1 | Result2 | undefined dla zmiennej result. O to Ci chodzi?

Całość na TypeScript Playground.

1
komentarz 18 lutego 2022 przez rafal.budzis Szeryf (85,260 p.)

Chodzi mi o to zeby zadziałało w taki sposób :) 
 

const result: Result1 = foo.method(obj1);

Ponieważ skoro do method przekazuje obiekt który jest typem Object1 to wiem że wejdzie w ifa i uruchomi funkcje method1 a method1 zawsze zwraca typ Result1. Chcę żeby zwracany typ był zawężony na podstawie typu parametru 

komentarz 18 lutego 2022 przez ScriptyChris Mędrzec (190,190 p.)

Choć jak patrzę, to ustawienie typu any lub unknown dla argumentu object daje ten sam efekt - TS pewnie wnioskuje zwracany typ dla method na podstawie użyć instanceof.

komentarz 18 lutego 2022 przez rafal.budzis Szeryf (85,260 p.)

W teorii mógłbym zrobić to tak poprzez deklarowanie recznie każdego powiazania.
 

    method<Object1>(object: TObjs<Object1>): Result1
    method<Object2>(object: TObjs<Object2>): Result2
    method<T>(object: TObjs<T>) {
        if(object instanceof Object1) {
            return this.method1(object)
        }
        if(object instanceof Object2) {
            return this.method2(object)
        }
        if(object instanceof Object3) {
            return this.method3(object)
        }
        return undefined;
    }

ale chciałbym zeby się bardziej automatycznie domyślił wiec szukam ;)

komentarz 18 lutego 2022 przez ScriptyChris Mędrzec (190,190 p.)
Ah, to nie zrozumiałem co chcesz zrobić. :) Tak z głowy nie wiem jak to osiągnąć, ale coś poeksperymentuję.
1
komentarz 18 lutego 2022 przez ScriptyChris Mędrzec (190,190 p.)

@rafal.budzis, hmm zdaje się, że dodanie:

method<T>(object: TObjs<T>): typeof object;

nad:

method<T>(object: TObjs<T>) {

rozwiązuje problem. Więc wychodzi na to, że byłeś blisko rozwiązania z ręcznym dopisaniem każdego dowiązania. :)

komentarz 19 lutego 2022 przez rafal.budzis Szeryf (85,260 p.)
Zaktualizowałem nieco przykład w pytaniu. U mnie ten typeof nie smiga. ponieważ funkcja zwraca mi Object1 zamiast Result1 ;) Myśle że obecny przykład bedzie bardziej życiowy i może lepiej zrozumiały ;) Mamy 3 figury geometryczne i odwiedzającego który zwraca długość boku. I dla kawdratu zwraca jedną liczbę a dla prostokąta dwie.
komentarz 19 lutego 2022 przez Ehlert Ekspert (212,670 p.)
Musisz zrobić unię i napisać type guardy.
komentarz 19 lutego 2022 przez rafal.budzis Szeryf (85,260 p.)
Czyli bez type guarda się nie da? kurcze lipa bo wiem jaki to typ i myślałem że to tylko kwestia tego że nie umiem tego zapisać a tworzenie wzorca Odwiedzający ma za zadanie od delegować odpowiedzialność sprawdzania typu na elementy które się odwiedza. Takie dodatkowe sprawdzanie nie zaprzecza trochę idei tego wzorca? :/

Podobne pytania

0 głosów
1 odpowiedź 129 wizyt
pytanie zadane 1 sierpnia 2019 w JavaScript przez rafal.budzis Szeryf (85,260 p.)
0 głosów
0 odpowiedzi 99 wizyt
pytanie zadane 4 października 2023 w JavaScript przez reaktywny Nałogowiec (40,990 p.)
+1 głos
0 odpowiedzi 161 wizyt

92,555 zapytań

141,404 odpowiedzi

319,560 komentarzy

61,941 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...