繁体   English   中英

Typescript:重载和检查泛型

[英]Typescript: overloading and checking against generic type

我有这个界面:

export interface ICRUDService<T extends IModel> {

    save(item: T): Promise<void>;
    save(items: T[]): Promise<void>;
    save(item: IRemoteData<T>): Promise<void>;
    save(items: IRemoteData<T>[]): Promise<void>;
    save(item: Partial<T>): Promise<void>;
}

和一个实现:

export abstract class AbstractCRUDServiceImpl<T extends IModel> implements ICRUDService<T> {

    async save(item: T): Promise<void>;
    async save(items: T[]): Promise<void>;
    async save(item: IRemoteData<T>): Promise<void>;
    async save(items: IRemoteData<T>[]): Promise<void>;
    async save(item: Partial<T>): Promise<void>;

    async save(item: any): Promise<void> {

        if (typeof item === T)
            // ...

    }
}

但它说:

'T' 仅指一种类型,但在此处用作值。ts(2693)'

解决这个问题的正确方法是什么?

请记住,当代码实际运行时,所有输入信息都将消失。 因此,您不能依靠类型来确定 object 在运行时是什么。

相反,您必须确定一个值是否与您想要的类型具有相同的特征

其次,您的实现 function 的参数应该是覆盖中每种类型的联合类型。


假设您的IModelIRemoteData设置如下:

interface IRemoteData<T> {
  remoteData: T
}
interface IModel {
  id: number
}

现在你会有一个这样的实现:

export abstract class AbstractCRUDServiceImpl<T extends IModel> implements ICRUDService<T> {
  async save(item: T): Promise<void>;
  async save(items: T[]): Promise<void>;
  async save(item: IRemoteData<T>): Promise<void>;
  async save(items: IRemoteData<T>[]): Promise<void>;
  async save(item: Partial<T>): Promise<void>;
  async save(items: IRemoteData<T> | T): Promise<void>; // Added this override

  async save(item: T | T[] | IRemoteData<T> | IRemoteData<T>[] | Partial<T>): Promise<void> {
    if (Array.isArray(item)) {
      // item is array in this scope, iterate over each item and save them
      for (const singleItem of item) {
        await this.save(singleItem)
      }
    } else {
      // item is not an array in this scope
      if ('id' in item) {
        item // T | Partial<T>
      } else {
        item // IRemoteData<T>
      }
    }
  }
}

在该条件的每个分支中,您将处理该类型。

请注意,您永远不会将其与类型进行比较,但您会查看它是否具有您想要的类型的功能。 您可以使用Array.isArray()来查看它是否是一个数组,并且在条件 typescript 中使用时知道这意味着它是一个数组,并且该类型不能再是联合中的任何非数组类型。

您可以'propName' in item来测试它是否定义了一个可能只存在于您想要的类型之一的属性。

然后您可以使用else子句来匹配您尚未过滤掉的任何类型。

操场


现在注意额外的覆盖:

async save(items: IRemoteData<T> | T): Promise<void>; // Added this override

这是条件的数组处理分支所需要的。 问题是,在你知道它是数组之后,你不知道它是什么数组。 所以在迭代项目时,每个项目的类型是:

T | IRemoteData<T>

所以你需要一个重载来处理那个特定的情况。

async save(items: IRemoteData<T> | T): Promise<void>; // Added this override

或者您可以完全消除覆盖。 当您只有一个可能是类型联合的参数时,覆盖就没有那么有用了,并且更有用的某些参数签名返回不同的类型。 这是单个 function 定义本身无法做到的事情。

例如:

function foo(a: number): string
function foo(a: string): number
function foo(a: number|string): number|string {
  if (typeof a === 'string') {
    return 123
  } else {
    return 'a string'
  }
}

此重载将某些参数类型与某些返回类型联系起来。 但是您的 function 不需要,并且可以表示为单个 function ,其中它的论点只是许多事物的结合。

所有这意味着这应该工作:

export abstract class AbstractCRUDServiceImpl<T extends IModel> {
  async save(item: T | T[] | IRemoteData<T> | IRemoteData<T>[] | Partial<T>): Promise<void> {
    if (Array.isArray(item)) {
      // item is array in this scope, iterate over each item and save them
      for (const singleItem of item) {
        await this.save(singleItem)
      }
    } else {
      // item is not an array in this scope
      if ('id' in item) {
        item // T | Partial<T>
      } else {
        item // IRemoteData<T>
      }
    }
  }
}

操场

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM