Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
TerenceZ opened this issue Apr 10, 2019 · 9 comments · Fixed by #36476
Closed

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

TerenceZ opened this issue Apr 10, 2019 · 9 comments · Fixed by #36476
Assignees
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue

Comments

@TerenceZ
Copy link

TerenceZ commented Apr 10, 2019

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.

@RyanCavanaugh RyanCavanaugh added the Bug A bug in TypeScript label Apr 10, 2019
@RyanCavanaugh RyanCavanaugh added this to the TypeScript 3.5.0 milestone Apr 10, 2019
@RyanCavanaugh
Copy link
Member

Simpler repro

// @noImplicitAny

// Error
const k1: any = (m = 3) => m;
// No error
const k2 = (m = 3) => m;

@RyanCavanaugh
Copy link
Member

@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?

@jeffryang24
Copy link

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.

@ahejlsberg
Copy link
Member

ahejlsberg commented Apr 30, 2019

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

@bdpartridge
Copy link

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.

@ahejlsberg
Copy link
Member

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

@bdpartridge
Copy link

@ahejlsberg That makes sense. Thanks for explaining.

@christopherthielen
Copy link

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

@ahejlsberg
Copy link
Member

@bdpartridge @christopherthielen The issues you point out above are now fixed in #36476.

@ahejlsberg ahejlsberg added the Fix Available A PR has been opened for this issue label Jan 28, 2020
@ahejlsberg ahejlsberg modified the milestones: Backlog, TypeScript 3.8.1 Jan 28, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants