【问题标题】:Incorrect inference when using the return keyword in an identity function, or when using an optional argument在恒等函数中使用 return 关键字或使用可选参数时推理不正确
【发布时间】:2022-07-31 18:09:04
【问题描述】:

我在使用身份函数时遇到了非常奇怪的行为。我正在编写一个带有模式的向导系统(附件是一个非常简化版本的游乐场链接),并使用受限标识函数进行推理。

问题出现在我使用以下任一属性时无法推断的属性之一:

  • 当标识函数的返回值使用return 关键字(而不是用括号括起来的单行返回)时。 或
  • 在标识函数中声明可选参数时。该参数在标识函数的类型定义中声明,并且在使用 Parameters<typeof myFunction> 时,无论是在声明参数还是未声明参数时,都可以正确推断。

这两个问题对我来说都非常奇怪,这意味着我要么遗漏了一些非常基本的东西,要么发现了 2 个罕见的错误。

这在所有可用的 Playground 版本(试用到 3.3.3)和 4.8 中都重现。

Playground link with relevant code

在操场上查看代码示例可能更好,但是:

类型声明:

type Schema = Record<string, unknown> // modified from original for the sake of the example, if it doesn't make sense

type StepFunction<
  TSchema extends Schema = Schema,
> = (anything: unknown) => {
  readonly schema: TSchema
  readonly toAnswers?: (keys: keyof TSchema) => unknown
}

function step<TSchema extends Schema = Schema>(
    stepVal: StepFunction<TSchema>,
  ): StepFunction<TSchema> {
    return stepVal
  }

示例: 注意所有函数的返回对象都是一样的!区别在于:

  • 我们是否使用 return 关键字 (?!?!)
  • 我们是否有step 函数的参数。不是说如果我做Parameters&lt;typeof myStepValue&gt;,即使参数丢失,它也会被正确推断(!)
// WORKS: `keys` is inferred based on the `schema`
// - no argument for `step` function
// - no `return` keyword
const workingExample = step(() => ({
  schema: {
    attribute: 'anything',
  },
  toAnswers: keys => {
    // RESULT: `keys` inferred successfully as `attribute`
    type Test = string extends typeof keys ? never : 'true'
    const test: Test = 'true'
    return { test }
  },
}))
// FAILS: `keys` is not inferred based on the `schema`
// - has argument for `step` function
const nonWorkingA = step(_something => ({
  schema: {
    attribute: 'anything',
  },
  toAnswers: keys => {
    // RESULT: `keys` failed to inferred hence defaults to `string`
    type Test = string extends typeof keys ? never : 'true'
    const test: Test = 'true'
    return { test }
  },
}))
// FAILS: `keys` is not inferred based on the `schema`
// - has `return` keyword rather than a "single-return" return with parentheses
const nonWorkingB = step(() => {
  return {
    schema: {
      attribute: 'anything',
    },
    toAnswers: keys => {
      // RESULT: `keys` failed to inferred hence defaults to `string`
      type Test = string extends typeof keys ? never : 'true'
      const test: Test = 'true'
      return { test }
    },
  }
})

【问题讨论】:

    标签: typescript types return arguments inference


    【解决方案1】:

    你可以简化你的类型:

    type Step<TSchema> = (store: TSchema) => {
      data: {
        schema: TSchema
        toAnswers?: (keys: keyof TSchema) => unknown
      }
    }
    
    function step<TSchema extends Schema>(x: Step<TSchema>) {
      return x
    }
    

    在我看来,有两个原因:

    • nonWorkingA 中有两个事实来源——一个来自参数,一个来自内部对象结构(参数_store 隐含any 类型),如果您指定_store 的类型,它应该可以工作李>
    • nonWorkingB 你使用statements(返回)而不是expressions 并且打字稿不能推断类型

    我已经为此工作了将近 3 个小时,但仍然无法弄清楚如何进行正常的类型推断

    【讨论】:

    • 感谢您为此投入时间! - 出于 2 个真实原因,我不确定是否是这种情况,因为如果我只用 toAnswers: key keyof TSchema 替换 toAnswers 函数 - 它的推断是正确的。它不能仅仅因为它是函数中的参数而起作用。 - 对于statmentsexpressions - 这在任何地方都有记录吗?找不到这种差异的任何参考。语法明智,这对我来说很奇怪,因为使用 ({}) 而不是 return 是语法糖 AFAIK。
    • @ShayDavidson 不,这不是语法糖。 () =&gt; ({}) - 是表达式(与() =&gt; 5 相同),但() =&gt; { return {} } 不是,因为我们在这里使用了块和返回语句。
    【解决方案2】:

    显然这是关于上下文相关表达式的 TypeScript 推理问题(请参阅 https://github.com/microsoft/TypeScript/issues/49951https://github.com/microsoft/TypeScript/issues/47599)。

    但是,我确实找到了一个临时解决方案 - 通过使用标识函数包装返回值,边缘情况可以工作。

    例如:

    step(_something => (identityOfStep({
      schema: {
        attribute: 'anything',
      },
      toAnswers: keys => {
        // RESULT: `keys` failed to inferred hence defaults to `string`
        type Test = string extends typeof keys ? never : 'true'
        const test: Test = 'true'
        return { test }
      },
    })))
    

    Example in a playground

    【讨论】:

      猜你喜欢
      • 2020-09-13
      • 2022-06-27
      • 1970-01-01
      • 2013-06-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-19
      相关资源
      最近更新 更多