我有这个界面:
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'仅指类型,但被用作值 here.ts(2693)'
解决这个问题的正确方法是什么?
答案 0 :(得分:1)
请记住,当代码实际运行时,所有键入信息都将消失。因此,您不能依靠类型来确定运行时对象是什么。
相反,您必须确定某个值是否具有与所需类型相同的 features 。
第二,您的实现函数的参数应该是一个类型,该类型是重写中每种类型的并集。
假设您的IModel
和IRemoteData
的设置如下:
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()
来查看它是否是一个数组,并且在有条件的打字稿中使用时知道这意味着它是一个数组,并且该类型在联合中不再可以是任何非数组类型。
您可以使用'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 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'
}
}
此重载将某些参数类型与某些返回类型联系在一起。但是您的函数不需要它,可以将其表示为一个函数,其中参数只是许多事物的结合。
这一切都应该起作用:
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>
}
}
}
}