【发布时间】:2020-01-23 05:17:50
【问题描述】:
我正在编写一个通用模式匹配函数match 用于标记联合,将对象作为匹配器传递(用于代替典型的switch (obj.kind) { ... })。这就是我所拥有的:
type UnionNamespace<Obj extends { kind: string }> = {
[K in Obj["kind"]]: Obj extends { kind: K } ? Obj : never;
};
type Matcher<Obj extends { kind: Kind }, Result, Kind extends string> = {
[K in Kind]: (obj: UnionNamespace<Obj>[K]) => Result;
};
function match<Obj extends { kind: Kind }, Result, Kind extends string>(
obj: Obj,
matcher: Matcher<Obj, Result, Kind>
): Result {
const fn = matcher[obj.kind];
return fn(obj as Parameters<typeof fn>[0]);
}
/* Example */
type Square = { kind: "square"; side: number };
type Circle = { kind: "circle"; radius: number };
type Shape = Square | Circle;
const square = { kind: "square", side: 2 } as Shape;
const surface = match(square, {
square: square => square.side ** 2,
circle: circle => Math.PI * circle.radius ** 2
});
console.log(surface.toFixed()); // Op that does not type-check if surface is not a number
我对代码并不完全满意。例如,1) 我不想提示Kind extends string,但后来我得到Result=unknown。另外,2) 这个Parameters<typeof fn>[0] 看起来有点笨拙,但这是我发现的唯一一种对调用进行类型检查的方法。
有什么想法/建议吗?你知道任何现有的代码可以做这样的事情吗?
[编辑] 最终版本,可选择区分字段:
https://gist.github.com/tokland/c0db1473cc9bfa924470e52bdac8450c
【问题讨论】:
标签: typescript pattern-matching