一个泛型Typescript函数是否可以有多个返回类型



我目前正在设计一个使用Typescript的web服务器,但遇到了死胡同。目标是拥有一个接口或类似的东西,让任何其他对系统其他部分没有深入了解的开发人员都可以进来,实现他们自己版本的解析器,并让它与系统其他部分一起工作。此外,我希望可以在不修改原始代码的情况下添加更多的返回类型。

目前,我已经定义了数据的形状,我正试图围绕解析器本身进行思考。数据如下:

export namespace CardData {
export interface Base {
type: string;
source: Source;
}
export interface Graph {
labels: string[];
data: number[];
}
// Other definitions below
}

解析器接口如下所示:

export interface Source {
id: number;
name: string;
url: string;
isReachable: boolean;
type: string;
}
export interface ConnectorArgument {
id: number;
key: string;
value: string;
}
export interface Parser {
// Unrelated code
// The type of T depends on args
getSource: <T>(
source: Source,
args: ConnectorArgument[]
) => Promise<T>;
}

但如果我实现getSource并尝试返回任何内容,我会得到:

'T' could be instantiated with an arbitrary type which could be unrelated to 'Graph'.

我理解为什么Typescript拒绝让它工作,因为T的类型是由函数的调用方决定的,但我还没能想出一个好的替代方案。

我看了其他问题,比如这里和这里,但没有一个真正适合我的问题。这甚至可以用Typescript实现吗?还是我最好使用其他语言?

编辑1:我添加了Source和ConnectorArgument的定义。

由于source参数中的字符串type属性应该决定整个返回类型,因此有一个TypeScript范式可以处理此映射。如果您想查阅更广泛的示例,它与addEventListener及其亲属在TypeScript的DOM声明中使用的相同。

在您的情况下,您需要在type字符串值和将为它们返回的实际类型之间创建一个映射接口。getSource的返回类型将是来自该映射的查找。不幸的是,由于这里描述的TypeScript中的一些限制,在返回每个可能的类型时都需要一个不雅的强制转换。以下是它的外观(例如,使用更简单的结构(:

interface Widget { foo: number }
interface Sprocket { bar: number }
interface SourceTypeMap {
'widget': Widget;
'sprocket': Sprocket;
}
interface Source {
type: keyof SourceTypeMap;
}
interface Parser {
getSource: <T extends keyof SourceTypeMap>(
source: Source & { type: T }
) => Promise<SourceTypeMap[T]>;
}
class ExampleParser implements Parser {
async getSource<T extends keyof SourceTypeMap>(
source: Source & { type: T }
): Promise<SourceTypeMap[T]> {
return ((): SourceTypeMap[keyof SourceTypeMap] =>
{
switch (source.type) {
case 'widget':
return { foo: 42 };
case 'sprocket':
return { bar: 42 };
}
throw new TypeError(`unsupported type '${source.type}'`);
}) () as SourceTypeMap[T];
}
}
async function test() {
const parser = new ExampleParser ();
const widget = await parser.getSource({ type: 'widget' }); // infers Widget type
console.log(widget); // { foo: 42 }
const sprocket = await parser.getSource({ type: 'sprocket' }); // infers Sprocket type
console.log(sprocket); // { bar: 42 }
}
test();

这是一个可在TypeScript Playground上运行的版本。

相关内容

最新更新