Skip to content

Case that infering type for parameters works for 3.3.0 but error in 3.4.0 #30840

Closed

Description

@TerenceZ

TypeScript Version: 3.4.0-dev.201xxxxx

Search Terms:

3.4.0 regression
parameter type infer

Code

// with compilerOptions.noImplicitAny=true in tsconfig.json
export function test<S>(options: { partial: Partial<S> | undefined }) {
  const fn: any = function (state = options.partial, action_: any) {
    return state
  }
  return fn
}

The related tsconfig.json

{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "allowJs": true,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "preserve"
  },
  "include": ["src"]
}

Expected behavior:
No error. It's worth noting that it works in 3.3.4.

Actual behavior:

With the tsc command line, it outputs the following errors:

error TS7006: Parameter 'state' implicitly has an 'any' type.

25   const fn: any = function(state = options.partial, action_: any) {

Playground Link:

Related Issues:


The code works in 3.3.4 with no errors, but after upgrading to 3.4.0, the tsc complains that the parameter has 'any' type when the LHS fn is of any type. If I replace fn: any = with fn =, it works for 3.4.0.

Activity

  1. RyanCavanaugh commented on Apr 16, 2019

    @RyanCavanaugh
    Member

    Simpler repro

    // @noImplicitAny
    
    // Error
    const k1: any = (m = 3) => m;
    // No error
    const k2 = (m = 3) => m;
  2. RyanCavanaugh commented on Apr 17, 2019

    @RyanCavanaugh
    Member

    Anders Hejlsberg (@ahejlsberg) Andrew and I were discussing this on teams and I disagree with part of the logic in #28816 (comment)

    However, for the function expression passed as an argument to id we do have a contextual type, namely T. Since T has no constraint we should end up inferring an implicit any type for the parameter, but this isn't quite working correctly.

    I don't see why this desirable at all. Unless there are call/construct signatures on the contextual type, this can only create new implicit anys where none were needed. This creates breaks in pretty basic examples (see above comments). What's the motivating example for taking an outer any contextual type (the one typing the function expression, not its parameter) and using that to add implicit anys to initialized parameters?

  3. jeffryang24 commented on Apr 17, 2019

    @jeffryang24

    Yes, after I upgraded the typescript package to 3.4, suddenly this error showed up and this should not be an issue before 3.4.

  4. ahejlsberg commented on Apr 30, 2019

    @ahejlsberg
    Member

    Ryan Cavanaugh (@RyanCavanaugh) Regarding this example:

    const k1: any = (m = 3) => m;  // m: implicit any
    const k2 = (m = 3) => m;  // m: number

    I would absolutely argue that it is correct for the inferred type of m in the first example to be an implicit any. We know from the contextual type that anything more specific we infer for m will be disregarded, and we have no idea what type of arguments will actually be passed for m (and keep in mind that the default value only kicks in when we're passed an undefined). In the second example it is fine to infer from the initializer because the inferred type will survive and constrain arguments that can be passed for m.

    Now, I think where this argument changes is when the contextual type is a generic type with a constraint. Just because the constraint says any doesn't mean that we can't infer a more specific type. So we may have some work to do there.

  5. bdpartridge commented on May 15, 2019

    @bdpartridge

    Possibly related to this, after upgrading from 3.3.1 to 3.4.5, I'm also getting implicit any errors for default parameters in a very specific case (see Case 3):

    declare function memoize<F extends Function>(func: F): F;
    
    // Case 1: No error
    function add(x: number, y = 0): number {
        return x + y;
    }
    const memoizedAdd = memoize(add);
    
    // Case 2: No error
    const add2 = (x: number, y = 0): number => x + y;
    const memoizedAdd2 = memoize(add2);
    
    // Case 3: Error - Parameter 'y' implicitly has an 'any' type
    const memoizedAdd3 = memoize((x: number, y = 0): number => x + y);

    Passing an arrow function expression directly to memoize (in Case 3) results in an implicit any error for default parameter y. For the other two cases, the type of y is correctly inferred as number.

    If this is unrelated to the original issue, I'd be happy to move this to a separate one.

  6. ahejlsberg commented on May 16, 2019

    @ahejlsberg
    Member

    Braeden Partridge (@bdpartridge) That's exactly the issue I allude to here in my comment:

    Now, I think where this argument changes is when the contextual type is a generic type with a constraint. Just because the constraint says any doesn't mean that we can't infer a more specific type. So we may have some work to do there.

    That's what we need to fix.

  7. bdpartridge commented on May 16, 2019

    @bdpartridge

    Anders Hejlsberg (@ahejlsberg) That makes sense. Thanks for explaining.

  8. christopherthielen commented on Jun 19, 2019

    @christopherthielen

    here's another surprising case:

    // no error
    const fn1: (x: number) => number = (x = 1) => x
    //error
    const fn2: <T>(x: number) => number = <T>(x = 1) => x
    
  9. ahejlsberg commented on Jan 28, 2020

    @ahejlsberg
    Member

    Braeden Partridge (@bdpartridge) Chris Thielen (@christopherthielen) The issues you point out above are now fixed in #36476.

  10. 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

Labels

BugA bug in TypeScriptFix AvailableA PR has been opened for this issue

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions