TypeScript 当"最佳匹配"包含上下文类型参数时,从候选函数中推断出空的rest参数类型 ```markdown 当"最佳匹配"包含上下文类型参数时,从候选函数中推断出空的rest参数类型, ```

jgwigjjp  于 4个月前  发布在  TypeScript
关注(0)|答案(4)|浏览(36)

Bug报告

🔎搜索词

Rest参数推断上下文

🕗版本和回归信息

  • 这是我在每个版本中尝试的行为,我查阅了关于类型推断的常见问题解答条目

⏯Playground链接

带有相关代码的Playground链接

💻代码

const doSomething = <Args extends unknown[]>(
    fn1: (aNumber: number, ...rest: Args) => void,
    fn2: (aNumber: number, ...rest: Args) => void,
) => {}

// OK, no contextual types
doSomething(
    (explicit: number, rest: string) => {}, // Args = [string]
    () => {},
)

// OK (both contain contextually typed arg)
doSomething(
    (contextual, rest: string) => {}, // Args = [string]
    (contextual) => {},
)

// Contextually type only fn1 - Not ok
doSomething(
    (contextual, rest: string) => {}, // ERROR
    () => {}, // Args = []
)
doSomething(
    (contextual, rest: string) => {}, // ERROR
    (explicit: number) => {}, // Args = []
)

🙁实际行为

当fn1(仅fn1)包含一个上下文类型参数时,它会被忽略,以fn2为优先。

🙂预期行为

在所有示例中,fn2完全没有指定任何rest参数。
我希望不指定任何rest参数的函数始终是最不受欢迎的候选者(权重最低?),因为“没有rest参数”可以总是分配给任何rest参数类型。

idfiyjo8

idfiyjo81#

为了防止bug报告过于抽象/简约,以下是我在实际使用场景中展示为什么这种行为在实践中感觉令人惊讶的示例。非常欢迎就是否有一种解决方法提示TS始终使用asyncOperation函数进行推断提出建议。
Playground链接

// React hook for async operations
const useAsync = <Args extends unknown[], Result>(
    asyncOperation: (
        progressListener: (progress: number) => void,
        ...args: Args
    ) => Promise<Result>,
    onSuccess?: (
        result: Result,
        ...args: Args
    ) => void,
) => { /* snip */ }

// Ok
const measureStringRequestOk = useAsync(
    async (_progressListener, stringToMeasure: string) => stringToMeasure.length,
    (length) => console.log(`String is ${length} chars long`)
)

// Ok
const measureStringRequestOk2 = useAsync(
    async (_progressListener, stringToMeasure: string) => stringToMeasure.length,
    (length, stringToMeasure) => console.log(`${stringToMeasure} is ${length} chars long`)
)

// Huh?
const measureStringRequestBroke = useAsync(
    async (_progressListener, stringToMeasure: string) => stringToMeasure.length, // Error!
    () => console.log("Measured the string!")
)
gwbalxhn

gwbalxhn2#

"经典"的NoInfer技巧在这里很有帮助:TSPlayground

hrirmatl

hrirmatl3#

这里的问题与上下文类型无关,我们可以在显式注解的函数(TS playground)中复现它:

const doSomething = <Args extends unknown[]>(
    fn1: (aNumber: number, ...rest: Args) => void,
    fn2: (aNumber: number, ...rest: Args) => void,
) => {}

doSomething(
    (contextual: number) => {},
    (contextual: number, rest: string) => {}, // error
)

它取决于将协变性候选项推入数组的顺序。你已经在使用上下文类型参数时体验过这一点,因为非上下文敏感函数的候选项首先被推送到那里。
当需要时,两个候选项都可用,但通过getCommonSubtype选择了最左边的一个。
要解决这个问题,编译器可能需要跟踪这些候选项的"来源",并使用你建议的附加启发式方法:
我希望没有指定任何剩余参数的函数始终是最不理想的剩余参数推理候选者(权重最低?),因为“没有剩余参数”可以始终分配给任何剩余参数类型。

ca1c2owp

ca1c2owp4#

啊,明白了。我原以为尝试在不使用上下文类型参数的情况下重新创建它会成功,但我想这就是当我试图在周五下午理解编译器行为时发生的事情!
非常感谢NoInfer提示,这将产生巨大的差异👍

相关问题