Skip to content

Parameter of a callback without a specified type next to it breaks code. #29799

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
anurbol opened this issue Feb 7, 2019 · 4 comments
Closed
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed

Comments

@anurbol
Copy link

anurbol commented Feb 7, 2019

TypeScript Version: 3.4.0-dev.20190207, 3.3.0

Search Terms:
type parameter, callback

Code

interface Bar<T> {
    <B_RETURN>(
        a: (aParam: B_RETURN) => any,
        b: (bParam: T) => B_RETURN // B_RETURN is supposed to be inferred from here
        ): any
}

declare const bar: Bar<any>

bar((aParam) => 1, () => 'abc') // OK! `aParam` is `string`
bar((aParam) => 1, (bParam) => 'abc') // `aParam` is `{}`. Why?
bar((aParam) => 1, (bParam: any) => 'abc') // `aParam` is `string`. Again, why?
let arg2 = (bParam) => 'abc' ; bar((aParam ) => 1, arg2) // `aParam` is `string`. Again, why?

Expected behavior:
aParam should be string in each of the four examples

Actual behavior:
aParam is for some reason {} instead of string in the second example

Playground Link: link

I would be very grateful if someone can give me some quick directions (Is it a bug? What am I missing?). My work depends on it in a great degree.

P.S. Asked on stackoverflow and got no answer.

@RyanCavanaugh RyanCavanaugh added the Design Limitation Constraints of the existing architecture prevent this from being fixed label Feb 14, 2019
@RyanCavanaugh
Copy link
Member

This is basically the dual of #29791 - inference on functions proceeds left-to-right, so during the inference of aParam in the first function we have not yet examined the body of the second function expression to determine its return type (which we believe will be context-sensitive, in other words depends on its parameter type).

@anurbol
Copy link
Author

anurbol commented Feb 15, 2019

@RyanCavanaugh Thank you, made it very clear for me. Except one part:

which we believe will be context-sensitive, in other words depends on its parameter type

I am not sure how the return type can depend on parameter type if it's clear that the function returns string, because it returns string literal. Moreover, I am confused by the fact that unknown parameter (bParam) and parameter of type any (bParam: any) lead to different results. Maybe this is worth fixing? I feel that it's wrong. It's just a suggestion, because, I am satisified with the provided answer already and this issue can be closed.

Again, thank you very much, I hate mysteries and love when they become simple things, like you made them for me.

@RyanCavanaugh
Copy link
Member

I am not sure how the return type can depend on parameter type if it's clear that the function returns string, because it returns string literal.

We don't have a separate pass to say "Go dive into the function and check to see if all its return statements don't rely on its parameter type" - doing so would be expensive in light of the fact that extremely few real-world functions actually behave like that in practice.

Moreover, I am confused by the fact that unknown parameter (bParam) and parameter of type any (bParam: any) lead to different results.

An unannotated parameter isn't the same as any -- it's one that we're going to try to infer a type for. Functions with type annotations aren't context-sensitive, thus get treated differently in the inference process.

@anurbol
Copy link
Author

anurbol commented Feb 16, 2019

@RyanCavanaugh Wow. That's another valuable insight of how the compiler works, thank you so much! If only there would be a way to know these algorithms of TS compiler without looking at it's source code and bothering you in issues.

I think I will close this issue as I don't have other questions and the "Design Limitation" badge probably means I should close it.

Thanks again!

@anurbol anurbol closed this as completed Feb 16, 2019
jeremija added a commit to rondomoon/rondo-framework that referenced this issue Jan 10, 2020
After spending almost two days in finding the issue, I ran across a few
TypeScript issues on their GitHub page:

- Loss of type inference converting to named parameters object
  microsoft/TypeScript#29791

- Parameter of a callback without a specified type next to it breaks code.
  microsoft/TypeScript#29799

- Convert to named parameters
  microsoft/TypeScript#30089

It became clear that TypeScript is unable to infer method return
arguments if a generic type is used more than once in generic parameter
object. Instead it returns {}.

For example, the following would fail on line 28:

    type Convert<A, B> = (value: A) => B

    interface IParams<C, D> {
      value: C
      convert: Convert<C, D>
      doConvert: (value: C, convert: this['convert']) => D
    }

    function doSomething<E, F>(value: E, convert: Convert<E, F>) {
      return convert(value)
    }

    function build<G, H>(params: IParams<G, H>) {
      const {value, convert} = params
      return params.doConvert(value, convert)
    }

    const outerResult = build({
      value: {
        a: {
          value: 1,
        },
        b: 'string',
      },
      convert: value => value.a,
      doConvert: (value, convert) => {
        const innerResult = doSomething(value, convert)
        innerResult.value
        console.log('innerResult:', innerResult)
        return innerResult
      },
    })

    console.log('outerResult:', outerResult)

With the message:

    Property 'value' does not exist on type '{}'.

If we replace parameter object IParams with regular ordered function
parameters, the compilation succeeds.

RyanCavanough (TS project lead) from GitHub commented:

> We don't have a separate pass to say "Go dive into the function and
> check to see if all its return statements don't rely on its parameter
> type" - doing so would be expensive in light of the fact that extremely
> few real-world functions actually behave like that in practice.

Source: microsoft/TypeScript#29799 (comment)

These modifications bring type safety to TestUtils.tsx, and therefore
client-side tests of React components, while keeping almost the same
ease of use as before.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Design Limitation Constraints of the existing architecture prevent this from being fixed
Projects
None yet
Development

No branches or pull requests

2 participants