Skip to content

Incorrect inference when using the return keyword in an identity function, or when using an optional argument #49951

Closed
@ShayDavidson

Description

@ShayDavidson

Bug Report

I'm running into very weird behavior when using identity functions. I'm writing a wizard system with schema (attached is a playground link with a very simplified version), and using constrained identity function to get inference.

The problem occurs in an argument of one property which is a function, when either of these occur:

  • When the returned value from the identity function is using the return keyword (rather than a single-line return wrapped with parentheses).
    OR
  • When declaring an optional argument in the identity function. The argument is declared in the type definition of the identity function, and when using Parameters<typeof myFunction> it's inferred correctly both when declaring the argument and when I not.

Both these issues are super weird to me, which means I'm either missing something very fundamental or I have found 2 rare bugs.

🔎 Search Terms

Inference, Return, Arguments, Parentheses, Identity Function.

🕗 Version & Regression Information

This reproduces in all available playground versions (tried down to 3.3.3), and also in 4.8.

⏯ Playground Link

Playground link with relevant code

💻 Code

Probably better to check the playground for code examples, but there:

TYPES DECLERATIONS:

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
  }

EXAMPLES:
Notice the returned object of all functions is the same! The differences are in:

  • whether we use the return keyword or not (?!?!)
  • whether we have the argument for the step function or not. Note that if I do Parameters<typeof myStepValue> even when the argument is missing, it's inferred correctly (!)
// 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 be 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 be inferred hence defaults to `string`
      type Test = string extends typeof keys ? never : 'true'
      const test: Test = 'true'
      return { test }
    },
  }
})

🙁 Actual behavior

Nested value's function argument cannot be inferred correctly.

If I change the property (toAnswers) from a function to a simple property, there are no inference issues.

🙂 Expected behavior

Nested value function argument should be inferred correctly regardless of the return keyword or declaring the arguments.

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions