如何根据输入类型在typescript中指定promise的返回类型



我正在尝试创建一个;"汇总";TypeScript中的服务,可以根据对象的类型保存对象,但TypeScript告诉我我在做非法的事情(TS2322(。

这是我的代码的简化版本;首先,我有一个枚举,它描述了我所拥有的所有对象类:

enum ThingType {
SomeThing = 'SomeThing',
OtherThing = 'OtherThing',
}

然后是一个描述基本类型的接口,以及几个派生类型:

interface IThing {
name: string;
type: ThingType;
}
interface ISomeThing extends IThing {
// properties of ISomeThing
}
interface IOtherThing extends IThing {
// properties of IOtherThing
}

对于每个派生类型,都有一个特定的服务来保存这些类型的对象:

function saveSomeThing(someThing: ISomeThing): Promise<ISomeThing> {
return Promise.resolve(someThing);
}
function saveOtherThing(otherThing: IOtherThing): Promise<IOtherThing> {
return Promise.resolve(otherThing);
}

问题

现在我想有一个单一的";"汇总";将保存操作委托给正确实现的服务:

function saveThing<T extends IThing>(thing: T): Promise<T> {
switch (thing.type) {
case ThingType.SomeThing: return saveSomeThing(thing as ISomeThing);
case ThingType.OtherThing: return saveOtherThing(thing as IOtherThing);
default: throw new Error('Unknown type: ' + thing.type);
}
}

TypeScript告诉我的return语句无效:

TS2322: Type 'Promise<ISomeThing>' is not assignable to type 'Promise<T>'.
  Type 'ISomeThing' is not assignable to type 'T'.
    'ISomeThing' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'IThing'.

这很公平——TypeScript不知道T将是switch的那个分支的ISomeThing——有没有办法告诉TypeScript它是(而不用求助于unknownany(?

有两种方法:

as any标记返回(由于函数本身是类型化的,因此不会丢失任何类型信息(

主要在这里,因为另一种方式相当麻烦,并且不会增加任何实际价值:

function saveThing<T extends IThing>(thing: T): Promise<T> {
switch (thing.type) {
case ThingType.SomeThing: return saveSomeThing(thing as ISomeThing) as any;
case ThingType.OtherThing: return saveOtherThing(thing as IOtherThing) as any;
default: throw new Error('Unknown type: ' + thing.type);
}
}

游乐场

使用类型保护函数和条件返回类型

function saveThing<T extends IThing>(thing: T): Promise<T extends ISomeThing ? ISomeThing : IOtherThing> {
type returnType = T extends ISomeThing ? ISomeThing : IOtherThing;
if(isISomeThing(thing)){
return saveSomeThing(thing) as Promise<returnType>;
}
if(isIOtherThing(thing)){
return saveOtherThing(thing) as Promise<returnType>;
}
throw new Error('Unknown type: ' + thing.type);
function isISomeThing(potentialISomeThing: IThing): potentialISomeThing is ISomeThing {
return potentialISomeThing.type === ThingType.SomeThing;
}
function isIOtherThing(potentialIOtherThing: IThing): potentialIOtherThing is IOtherThing {
return potentialIOtherThing.type === ThingType.OtherThing;
}
}

游乐场

最新更新