具有通用方法和继承的打字稿工厂,错误不可分配给类型

Posted

技术标签:

【中文标题】具有通用方法和继承的打字稿工厂,错误不可分配给类型【英文标题】:Typescript factory with generic method and inheritance, error is not assignable to type 【发布时间】:2020-12-14 09:45:56 【问题描述】:

为什么打字稿编译器不接受以下代码? (我试图让代码不言自明)。

我收到以下错误 Type 'Dog' is not assignable to type 'K'Type 'Drone' is not assignable to type 'K'

我知道我可以通过使用联合类型 Dog | Drone 作为我工厂的输出类型来使用解决方法,但这意味着更新每个创建的新对象的签名。我想知道是否有一种“通用”的方式来声明这一点。

interface executeFn<I> 
  (param: I): string


interface Robot<I> 
  execute: executeFn<I>


interface BaseOrder 
  name: string


interface DogOrder extends BaseOrder 
  action: string


interface DroneOrder extends BaseOrder 
  destination: string


class Dog implements Robot<DogOrder> 
  execute(order: DogOrder): string 
    return 'The action is ' + order.action
  


class Drone implements Robot<DroneOrder> 
  execute(order: DroneOrder): string 
    return 'The destination is ' + order.destination
  


function robotFactory<T extends BaseOrder, K extends Robot<BaseOrder>>(
  param: T
): K 
  if (((param as unknown) as DogOrder).action) 
    return new Dog()
  
  return new Drone()

请注意,我也尝试更改泛型方法签名,但这并不能解决 pb。

interface executeFn<I> 
  <T extends I>(param: T): string

【问题讨论】:

【参考方案1】:

编译器会正确警告您 K 可能并不总是 Dog 或 Drone。

class DroneAdversary implements Robot<DroneOrder> 
  execute(order: DroneOrder): string 
    return 'The destination is ' + order.destination
  


const adversary = robotFactory<DroneOrder, DroneAdversary>(
  name: 'aaa',
  destination: 'dest'
); // adversary type is DroneAdversary

我会超载:

function isDogOrder(order: BaseOrder): order is DogOrder 
  return !!((order as DogOrder).action);


function robotFactoryOverload(order: DogOrder): Dog
function robotFactoryOverload(order: DroneOrder): Drone
function robotFactoryOverload(order: DogOrder | DroneOrder): Dog | Drone 
  if (isDogOrder(order)) 
    return new Dog()
   else 
    return new Drone()
  


const x = robotFactoryOverload(
  name: 'aaa',
  destination: 'sasas'
);

【讨论】:

Thx @Lesiak,使用联合类型 Dog | Drone 作为返回类型,我已经提到了这个潜在的解决方案,但这意味着每次添加新类型的机器人时都要更新签名。我希望有一个解决方案,我不必这样做。 然后为联合 type Robot = Dog | Drone 添加一个类型。你只有一个地方可以更新。 感谢type predicate,我不知道我们可以在打字稿中做到这一点***.com/questions/40081332/…【参考方案2】:

为了扩展 Lesiak 的答案,如果您定义 Robot 类型联合,则可以自动定义所有重载,如果您可能有很多重载,这会有所帮助。

type AnyRobot = Dog | Drone
type RobotForOrder<O extends BaseOrder, R = AnyRobot> = R extends Robot<O> ? R : never

function robotFactory<T extends BaseOrder> (order: T): RobotForOrder<T> 
function robotFactory<T extends BaseOrder> (order: T): AnyRobot 
  if (isDogOrder(order)) 
    return new Dog()
   
  return new Drone()

【讨论】:

以上是关于具有通用方法和继承的打字稿工厂,错误不可分配给类型的主要内容,如果未能解决你的问题,请参考以下文章

打字稿:类型'null'不可分配给类型错误

打字稿,JSON.parse 错误:“类型 'null' 不可分配给类型 'string'。”

使用反应成帧器类型'()=> void'的打字稿错误不可分配给类型'未定义'

打字稿错误:类型“文档 []”不可分配给自定义类型

打字稿 - 字符串'不可分配给类型'FC

打字稿错误:类型 'string' 不可分配给类型 '"allName" | `allName.$number.nestedArray`' 在反应钩子形式