我想构建一个看起来像这样的API:
export function GET({response}) {
return response
.match("text/html", "<p>Hello world</p>"),
.match("text/plain", "Hello world"),
.match("application/json", '{"message": "Hello world"}');
}
我有一个递归函数的开始:
function makeMatch(list) {
if(list.length === 0) {
return "end"
}
return {
match: item => makeMatch(list.filter(x => x !== item))
}
}
这是这样用的:
const items = ["a", "b", "c"];
makeMatch(items).match("a").match("b").match("c"); // -> "end"
以下是我试图用TypeScript实现的目标:
makeMatch(items).match("a"); // ok
makeMatch(items).match("x"); // does not type check, "x" is not in ["a", "b", "c"]
makeMatch(items).match("a").match("b").match("a") // does not type check, you already took "a"
我已经走到了这一步:
// makeMatch(emptyArray) returns "end"
function makeMatch<T>(list: []): "end"
// makeMatch(notEmptyArray) recurses
function makeMatch<T>(list: T[]): T extends "never" ? "end" : { match: <Item extends T>(item: Item) => ReturnType<typeof makeMatch<Exclude<T, Item>>> }
function makeMatch<T>(list: T[])
{
if(list.length === 0) {
return "end"
}
return {
match: (item: T) => makeMatch(list.filter(x => x !== item))
}
}
但是当我尝试使用该功能时...
const x = makeMatch<"json" | "text" | "csv">(["json", "text", "csv"])
我收到此错误:
This expression is not callable.
Each member of the union type `'(<Item extends "json">(item: Item) => Exclude<"json", Item> extends "never" ? "end" : { match: <Item extends Exclude<"json", Item>>(item: Item) => Exclude<Exclude<"json", Item>, Item> extends "never" ? "end" : { ...; }; }) | (<Item extends "text">(item: Item) => Exclude<...> extends "never" ? "end" : { ...; }) | (<I...'` has signatures, but none of those signatures are compatible with each other.(2349)
我试图用 TypeScript 完成的事情可能吗?如果是这样,如何?
TypeScript 通常无法理解复杂的函数体逻辑。因此,当您返回多个不同的东西时,例如"end"
if (items.length === 0){
return "end"
}
也是一个具有函数的对象match()
return {
match: <I extends T>(item: I) =>
makeMatch<Exclude<T, I>>(items.filter((x): x is Exclude<T, I> => x !== item))
}
TypeScript 将无法遵循此条件逻辑,而只会推断两种情况的并集作为返回类型。因此,我们必须为函数创建自己的条件返回类型,该类型将能够根据输入区分结果。
泛型类型MakeMatchReturn
涵盖这两种情况:
type MakeMatchReturn<T> = [T] extends [never]
? "end"
: {
match: <I extends T>(item: I) => MakeMatchReturn<Exclude<T, I>>
}
T
是传递数组的元素的联合。如果联合中没有剩余的元素,我们可以检查T
是否扩展never
.它包装在元组中以避免联合元素的分布。
生成的makeMatch
函数将按如下方式实现:
function makeMatch<T extends string>(items: readonly T[]): MakeMatchReturn<T> {
if (items.length === 0){
return "end" as unknown as MakeMatchReturn<T>
}
return {
match: <I extends T>(item: I) =>
makeMatch(items.filter((x): x is Exclude<T, I> => x !== item))
} as MakeMatchReturn<T>
}
T
是传递给函数的数组元素的联合。还有泛型类型I
它是传递给匹配函数的元素。当我们递归调用makeMatch
函数时,我们必须使用Exclude
从T
中删除当前元素I
。
另请注意,我们必须在此处使用as const
。否则,元组将被扩大为不再包含有关单个元素的任何信息的string[]
。
const items = ["a", "b", "c"] as const;
const result = makeMatch(items).match("a").match("b").match("c")
// ^? "end"
操场