TypeScript函数泛型仅可用于具有多个签名的函数重载

时间:2019-06-19 03:30:49

标签: typescript generics overloading

我正在定义一个具有通用功能的接口,例如:

export interface IState {
  send: <I, E>(message: I, callback?: (e: E) => void) => IState;
}

它对于具有多个签名的类运行良好:

class Left implements IState {
  send(m: 'go-on', cb?: (e: never) => void): Left;
  send(m: 'turn-right', cb?: (e: never) => void): Right;
  send(m: 'go-on' | 'turn-right', cb?: any) {
    return m === 'go-on' ? new Left() : new Right();
  }
}

class Right implements IState {
  send(m: 'go-on', cb?: (e: never) => void): Right;
  send(m: 'turn-left', cb?: (e: never) => void): Left;
  send(m: 'go-on' | 'turn-left', cb?: any) {
    return m === 'go-on' ? new Right() : new Left();
  }
}

type Both = Left | Right;

function test(l: Both) {
  if (l instanceof Left) {
    l.send('turn-right')
      .send('turn-left')
      .send('turn-right')
      .send('turn-left');
  }
  const l2 = new Left();
  l2.send('go-on')
    .send('turn-right')
    .send('turn-left');
  l2.send('turn-right').send('turn-left');
}

但是,当我想定义一个只有一个发送签名的IState时, 我遇到了编译错误:

class CountState implements IState {
  constructor(public readonly data: number) {}
  // send(m: 'inc', cb?: (e: number) => void): CountState;
  // send(m: 'inc', cb?: (e: number) => void): CountState;
  send(m: 'inc', cb?: (e: number) => void): CountState {
    const result = this.data + 1;
    if (cb !== undefined) {
      cb(result);
    }
    return new CountState(this.data + 1);
  }
}

发送方法出错:

  

类型为“ CountState”的属性“发送”不能分配给同一对象   基本类型为“ IState”的属性。输入'((m:“ inc”,cb ?:((e:number)   =>无效)| undefined)=> CountState'不能分配给类型'(message:I,callback ?:((e:E)=> void)| undefined)=> IState'。       参数'm'和'message'的类型不兼容。         ts(2416)

不能将类型“ I”分配给类型“ inc”。

如果我添加这两个注释行,例如

class CountState implements IState {
  constructor(public readonly data: number) {}
  send(m: 'inc', cb?: (e: number) => void): CountState;
  send(m: 'inc', cb?: (e: number) => void): CountState;
  send(m: 'inc', cb?: (e: number) => void): CountState {
    const result = this.data + 1;
    if (cb !== undefined) {
      cb(result);
    }
    return new CountState(this.data + 1);
  }
}

它可以编译,但是看起来确实很奇怪。我该如何解决?

1 个答案:

答案 0 :(得分:2)

我同意Titian Cernicova-Dragomir的观点,这似乎是编译器错误。 IState的定义基本上指出“发送”属性是一个可以用任何类型的“消息”调用的函数,而回调参数“ e”也可以具有任何类型。

export interface IState {
    send: <I, E>(message: I, callback?: (e: E) => void) => IState;
}

同时,在示例用法中,您明确列出了可能的类型,这些类型与接口定义相矛盾。如果通过编译很奇怪。

class Left implements IState {
    send(m: 'go-on', cb?: (e: never) => void): Left;
    send(m: 'turn-right', cb?: (e: never) => void): Right;
    send(m: 'go-on' | 'turn-right', cb?: any) {
        return m === 'go-on' ? new Left() : new Right();
    }
}

看看所包含的测试代码,无论如何,您都在检查未知的“两者”类型的确切类型,因此即使您只是在类中定义单独的方法,似乎也没有功能损失和正确的每个动作。 eq:

class Left {
    turnRight(...) {
        return new Right();
    }
    keepGoin(...) {
        return new Left();
    }
}

与对每个操作使用通用的“发送”方法相反。