Skip to content

Failed function argument type inference inside nested object passed as argument #22715

Closed

Description

@beshanoe

TypeScript Version: 2.7.1

Search Terms: infer

Code

interface IFooOptions<K extends string> {
  name?: string;
  value?: number;
  allFoos?: {[key in K]?: boolean }
  fn?: (allFoos: {[key in K]: string }) => void;
}

class Bar<T> {
  constructor(foos: {[key in keyof T]: IFooOptions<keyof T> }) {
    // ...
  }
}

const bar = new Bar({
  firstFoo: {
    name: "john",
    value: 3,
    allFoos: { // here allFoos infered
      firstFoo: true,
      nonexist: 'yep'
    }
  },
  secondFoo: {
    name: 'sandra',
    fn: allFoos => { // but here allFoos is any though the type of "fn" is infered correctly
      allFoos.nonexist = 2
    }
  }
});

const myFoo: IFooOptions<'one' | 'two'> = {
  name: 'adas',
  fn: allFoos => { //allFoos is infered right
    allFoos.nonexist = 2
  }
}

Expected behavior:

The allFoos argument inside fn field in the new Bar argument should have a type of

{
  firstFoo: string
  secondFoo: string
}

Actual behavior:

allFoos type is implicit any
Also it repoduces without using a class, just with plain generic function

Playground Link:
https://www.typescriptlang.org/play/index.html#src=interface%20IFooOptions%3CK%20extends%20string%3E%20%7B%0A%20%20name%3F%3A%20string%3B%0A%20%20value%3F%3A%20number%3B%0A%20%20allFoos%3F%3A%20%7B%5Bkey%20in%20K%5D%3F%3A%20boolean%20%7D%0A%20%20fn%3F%3A%20(allFoos%3A%20%7B%5Bkey%20in%20K%5D%3A%20string%20%7D)%20%3D%3E%20void%3B%0A%7D%0A%0Aclass%20Bar%3CT%3E%20%7B%0A%20%20constructor(foos%3A%20%7B%5Bkey%20in%20keyof%20T%5D%3A%20IFooOptions%3Ckeyof%20T%3E%20%7D)%20%7B%0A%20%20%20%20%2F%2F%20...%0A%20%20%7D%0A%7D%0A%0Aconst%20bar%20%3D%20new%20Bar(%7B%0A%20%20firstFoo%3A%20%7B%0A%20%20%20%20name%3A%20%22john%22%2C%0A%20%20%20%20value%3A%203%2C%0A%20%20%20%20allFoos%3A%20%7B%20%2F%2F%20here%20allFoos%20infered%0A%20%20%20%20%20%20firstFoo%3A%20true%2C%0A%20%20%20%20%20%20nonexist%3A%20'yep'%0A%20%20%20%20%7D%0A%20%20%7D%2C%0A%20%20secondFoo%3A%20%7B%0A%20%20%20%20name%3A%20'sandra'%2C%0A%20%20%20%20fn%3A%20allFoos%20%3D%3E%20%7B%20%2F%2F%20but%20here%20allFoos%20is%20any%20though%20the%20type%20of%20%22fn%22%20is%20infered%20correctly%0A%20%20%20%20%20%20allFoos.nonexist%20%3D%202%0A%20%20%20%20%7D%0A%20%20%7D%0A%7D)%3B%0A%0Aconst%20myFoo%3A%20IFooOptions%3C'one'%20%7C%20'two'%3E%20%3D%20%7B%0A%20%20name%3A%20'adas'%2C%0A%20%20fn%3A%20allFoos%20%3D%3E%20%7B%20%2F%2FallFoos%20is%20infered%20right%0A%20%20%20%20allFoos.nonexist%20%3D%202%0A%20%20%7D%0A%7D

Activity

  1. RyanCavanaugh commented on Mar 20, 2018

    @RyanCavanaugh
    Member
  2. ghost added
    BugA bug in TypeScript
    on Mar 22, 2018
  3. beshanoe commented on Mar 22, 2018

    @beshanoe
    Author

    Andy (Andrewkraft) (@Andy-MS) regarding argument, I was saying about this place:

    secondFoo: {
        name: 'sandra',
        fn: allFoos => { // but here allFoos is any though the type of "fn" is infered correctly
          allFoos.nonexist = 2
        }
      }

    if you remove error line "nonexistent": "yep", you will see that fn's type is inferred when I hover it, however allFoos argument is any
    image

  4. ghost removed
    BugA bug in TypeScript
    on Mar 22, 2018
  5. ghost added
    Needs InvestigationThis issue needs a team member to investigate its status.
    on Mar 22, 2018
  6. beshanoe commented on Apr 16, 2018

    @beshanoe
    Author

    Andy (Andrewkraft) (@Andy-MS) could you please explain your latest statement? Is it a bug? To me it looks like it is :)
    I've encountered the same thing again, and it really prevents me from doing good typing in my app.
    It's reproduced in 2.8.1 version
    Here's is another example:
    https://www.typescriptlang.org/play/index.html#src=class%20RouterProps%3CP%3E%20%7B%0D%0A%20%20props%3A%20P%0D%0A%7D%0D%0A%0D%0Aclass%20Component%3CP%3E%20%7B%0D%0A%20%20props%3A%20P%0D%0A%7D%0D%0A%0D%0A%2F%2F%20in%20'resolve'%20field%20I%20want%20to%20pass%20an%20object%20with%20keys%20from%20passed%20component's%20props%20and%20with%20functions%20as%20values%0D%0Ainterface%20IRoute%3CP%20%3D%20any%2C%20RP%20%3D%20any%3E%20%7B%0D%0A%20%20name%3A%20string%0D%0A%20%20component%3A%20Component%3CP%20%26%20RouterProps%3CRP%3E%3E%2C%0D%0A%20%20resolve%3F%3A%20%7B%20%5Bname%20in%20keyof%20P%5D%3F%3A%20(params%3A%20RP)%20%3D%3E%20Promise%3CP%5Bname%5D%3E%20%7D%0D%0A%7D%0D%0A%0D%0Afunction%20route%3CP%2C%20RP%3E(route%3A%20IRoute%3CP%2C%20RP%3E)%20%7B%0D%0A%20%20return%20route%0D%0A%7D%0D%0A%0D%0Aconst%20cmp%20%3D%20new%20Component%3C%7B%20foo%3A%20number%2C%20bar%3A%20string%20%7D%20%26%20RouterProps%3C%7B%20id%3A%20string%20%7D%3E%3E()%0D%0A%0D%0A%0D%0A%2F%2F%20if%20you%20hover%20'resolve'%20or%20'foo'%20object's%20field%2C%20the%20type%20is%20inferred%20correctly%2C%20saying%20that%20'params'%20arg%20is%20of%20type%20%7Bid%3A%20string%7D%0D%0A%2F%2F%20but%20when%20you%20hover%20'params'%2C%20it%20says%20that%20it's%20'any'%0D%0Aroute(%7B%0D%0A%20%20name%3A%20'sample-route'%2C%0D%0A%20%20component%3A%20cmp%2C%0D%0A%20%20resolve%3A%20%7B%0D%0A%20%20%20%20foo%3A%20params%20%3D%3E%20%7B%0D%0A%20%20%20%20%20%20alert(params.nonexistent)%20%2F%2F%20no%20error%20here%2C%20but%20should%20be%0D%0A%20%20%20%20%20%20return%20Promise.resolve(10)%20%2F%2F%20notice%20that%20the%20type%20of%20return%20value%20is%20enforced%2C%20namely%20change%2010%20to%20%22somestring%22%20and%20there%20will%20be%20an%20error%0D%0A%20%20%20%20%7D%0D%0A%20%20%7D%0D%0A%7D)

  7. beshanoe commented on Apr 16, 2018

    @beshanoe
    Author

    Andy (Andrewkraft) (@Andy-MS) cool thanks, do you think this issue is a "good first issue"? Or it's too complicated? I just want to try to start contributing to TS :)

  8. RyanCavanaugh commented on Sep 16, 2019

    @RyanCavanaugh
    Member

    This has been fixed (verified in 3.6)

  9. locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions