TypeScript 中带有泛型的条件类型

Fin*_*ber 17 generics typescript

我想要实现的目标可以用代码最好地解释:

给定鞋子和服装类别:

class Shoe {
    constructor(public size: number){}
}
Run Code Online (Sandbox Code Playgroud)
class Dress {
    constructor(public style: string){}
}
Run Code Online (Sandbox Code Playgroud)

有一个只能容纳鞋子或连衣裙的通用盒子。不能同时包含两者:

class Box <T extends Shoe | Dress > {
}
Run Code Online (Sandbox Code Playgroud)

然后有一个实用程序类来处理移动鞋子:

class ShoeMover {
    constructor(public size: number[]){}
}
Run Code Online (Sandbox Code Playgroud)

另外,还有一个用于移动连衣裙的实用程序类:

class DressPacker {
    constructor(public style: string[]){}
}
Run Code Online (Sandbox Code Playgroud)

然后有一个通用的移动器,如果用Box<Shoe>or实例化,则Box<Dress>有一个mover使用 theShoeMover或 the 的方法DressPacker

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

那么编译时保证应该是,如果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"]))
Run Code Online (Sandbox Code Playgroud)

我尝试使用条件类型,但我想涉及泛型的事实使得预期的解决方案不起作用。基本上这是我尝试过的:

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;
Run Code Online (Sandbox Code Playgroud)

然后更改 from 的定义mover

public mover(tool: ShoeMover | DressPacker) {
}
Run Code Online (Sandbox Code Playgroud)

public mover(tool: MoverFromEitherShoeOrDressB) {
}

or 

public mover(tool: MoverFromEitherShoeOrDressA) {
}
Run Code Online (Sandbox Code Playgroud)

..但是这些并没有提供我所寻求的编译时间保证。

有人知道如何实现这一目标吗?

编辑。

接受的答案适用于上述场景。但有一个稍微不同的场景是行不通的。我决定更新,而不是创建另一个问题。这种情况是当 的构造函数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"]))
Run Code Online (Sandbox Code Playgroud)

游乐场链接

leo*_*ory 15

你就快到了,你只需要在mover方法中也使用泛型,否则它不会知道T是什么。将泛型类型视为采用泛型 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>) {
    }
}
Run Code Online (Sandbox Code Playgroud)

此外,我更改了Move定义以排除Box泛型,因为您可以轻松地将其封装在类内部定义中,但您的解决方案也可以使用:

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

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

Run Code Online (Sandbox Code Playgroud)

编辑:在此处添加游乐场:游乐场链接