基于字符串参数的 Typescript 条件返回类型

9

当使用字符串字面量的联合作为输入参数时,如何移除强制转换并将类型放入函数头中

const get = <T extends "barcode" | "mqtt">(s: T) =>
    s === "barcode" ?
        <T extends "barcode" ? {scan: () => string} : {pan: () => string}>{scan: () => "we are scanning"} :
        <T extends "barcode" ? {scan: () => string} : {pan: () => string}>{pan: () => "we are panning"}

get("barcode").scan() // OK
get("mqtt").pan()     // OK
get("barcode").pan() // Error

我遇到了这个问题,试图回答别人的问题:https://dev59.com/9ozda4cB1Zd3GeqPl2Ne#55059318

1个回答

16
在这种情况下,最清晰的解决方案(虽然与类型断言一样不安全)是使用重载。您可以在公共签名中使用条件类型,在实现签名中使用简单联合。您需要切换到函数声明,因为函数表达式(箭头或常规)不容易支持重载。
function get<T extends "barcode" | "mqtt">(s: T): T extends "barcode" ? { scan: () => string } : { pan: () => string }
function get(s: "barcode" | "mqtt"): { scan: () => string } | { pan: () => string } {
    return s === "barcode" ?
        { scan: () => "we are scanning" } :
        { pan: () => "we are panning" }
}

get("barcode").scan() // OK
get("mqtt").pan()     // OK
get("barcode").pan() // Error

这样会更好,虽然不完全是我所希望的。在实现中必须指定返回类型吗? - Jørgen Tvedt
@JørgenTvedt 我认为你可以从实现中删除它。如果条件分支不在返回的联合中,您将收到错误提示。 - Titian Cernicova-Dragomir
好的,但我仍然不明白为什么我的代码没有强制转换就无法工作,这就是为什么我犹豫标记它作为答案的原因。 - Jørgen Tvedt
1
由于TypeScript无法推断包含未解析类型参数的条件类型。除非在非常有限的情况下,它只会检查精确的类型匹配。@JørgenTvedt - Titian Cernicova-Dragomir
好的,如果我将条件类型放到函数的返回类型中 - typescript 就无法自动转换或运算表达式。在我的想法中 - 类型保护的返回值根本不应该是联合类型,而应该是一个条件类型。那样不是更正确吗? - Jørgen Tvedt
1
@JørgenTvedt TypeScript并没有达到这个级别的流分析。流分析只会影响受保护块内变量的类型,它无法帮助编译器推断任何条件类型。 - Titian Cernicova-Dragomir

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接