Условные типы с обобщениями в TypeScript - PullRequest
3 голосов
/ 01 мая 2020

То, чего я хочу достичь, лучше всего объяснить в коде:

Данный класс обуви и одежды:

class Shoe {
    constructor(public size: number){}
}
class Dress {
    constructor(public style: string){}
}

Иметь коробку generi c, которая может содержать только Обувь или платье. Не может содержать оба:

class Box <T extends Shoe | Dress > {
}

Тогда есть вспомогательный класс, который заботится о движущейся обуви:

class ShoeMover {
    constructor(public size: number[]){}
}

Также, вспомогательный класс для перемещения платьев:

class DressPacker {
    constructor(public style: string[]){}
}

Затем используйте универсальный движок c, который при создании экземпляра с помощью Box<Shoe> или Box<Dress> имеет метод mover, который использует либо ShoeMover, либо DressPacker:

class Move<B extends Box<Shoe> | Box<Dress>> {
    private box: B;
    constructor(toMove: B) {
        this.box = toMove;
    }
    public mover(tool: ShoeMover | DressPacker) {
    }
}

Тогда гарантия времени компиляции должна заключаться в том, что, если Move создается с Box<Shoe>, то метод mover должен принимать только ShoeMover. Если создается с Box<Dress>. mover метод должен принимать только DressPacker. То есть:

let shoemover = new Move(new Box<Shoe>());

// compile
shoemover.mover(new ShoeMover([21]))

// should not compile. But currently does
shoemover.mover(new DressPacker(["1"]))

Я пытался использовать условные типы, но я предполагаю, что тот факт, что используются дженерики, заставляет задуманное решение не работать. В основном это то, что я пробовал:

type MoverFromEitherShoeOrDressA<T> =
    T extends Box<infer U> ?
        U extends Shoe ? ShoeMover :
        U extends Dress ? DressPacker :
        never:
    never;

and 

type MoverFromEitherShoeOrDressB<T> =
    T extends Box<Shoe> ? ShoeMover:
    T extends Box<Dress> ? DressPacker:
never;

Затем изменив определение mover с:

public mover(tool: ShoeMover | DressPacker) {
}

на

public mover(tool: MoverFromEitherShoeOrDressB) {
}

or 

public mover(tool: MoverFromEitherShoeOrDressA) {
}

.. но эти не дал гарантий времени компиляции, которые я искал.

Кто-нибудь знает, как этого добиться?

Редактировать.

Принятый ответ работает для сценарий выше. Но есть немного другой сценарий, который не работает. Вместо того, чтобы создавать другой вопрос, я решил обновить. Сценарий заключается в том, что конструктор Move изменяется на объединенный тип.

type Mover<T> = 
  T extends Shoe ? ShoeMover : 
  T extends Dress ? DressPacker : 
  never; 

class Move<T extends Shoe | Dress> {
    private box: Box<T>;
    constructor(public toMove: Box<Shoe>[] | Box<Dress>[]) {
        this.box = toMove;
    }
    public mover(tool: Mover<T>) {
    }
}


let shoemover = new Move(new Array<Box<Shoe>>());

// compile
shoemover.mover(new ShoeMover([21]))

// should not compile. But currently does
shoemover.mover(new DressPacker(["1"]))

Playground Link

1 Ответ

1 голос
/ 01 мая 2020

Вы были почти там, вам просто нужно использовать дженерики в методе mover, иначе он не будет знать, что такое T. См. Тип generi c как метод, который принимает в качестве параметра шаблон c T, а <> - ():

type Mover<T> = 
  T extends Shoe ? ShoeMover : 
  T extends Dress ? DressPacker : 
  never; 

class Move<T extends Shoe | Dress> {
    private box: Box<T>;
    constructor(toMove: Box<T>) {
        this.box = toMove;
    }
    public mover(tool: Mover<T>) {
    }
}

Более того, я изменил определение Move на исключите Box generi c, так как вы можете легко инкапсулировать его во внутренние определения класса, но ваше решение будет также работать с:

type MoverFromEitherShoeOrDressA<T> =
    T extends Box<infer U> ?
        U extends Shoe ? ShoeMover :
        U extends Dress ? DressPacker :
        never:
    never;

public mover(tool: MoverFromEitherShoeOrDressA<B>) { // <-- Here
}

Редактировать: добавлена ​​игровая площадка здесь: Playground Link

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...