Skip to content
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

Using arow function without explicit return not type checking correctly #60856

Open
KieranP opened this issue Dec 26, 2024 · 1 comment
Open

Comments

@KieranP
Copy link

KieranP commented Dec 26, 2024

πŸ”Ž Search Terms

"arrow function incorrect typing"

πŸ•— Version & Regression Information

Version 5.7.2 install via Yarn

⏯ Playground Link

No response

πŸ’» Code

Main functions

const client = new ApolloClient({ link, cache, defaultOptions })

export const subscribe = <T>(
  graphql: DocumentNode,
  variables: object,
): Observable<FetchResult<MaybeMasked<T>>> =>
  client.subscribe<T>({
    query: graphql,
    variables,
  })

Using subscribe in arrow function without assigning value first gives no type error:

export const watchPost = (
  variables: object,
  graphql: string,
): Observable<FetchResult<WatchPostResponse>> =>
  subscribe(
    gql`
    subscription watchPost($uuid: ID!) {
      postUpdated(uuid: $uuid) {
        ${graphql}
      }
    }
    `,
    variables,
  )

Assigning to a const and then returning it gives type error:

export const watchPost = (
  variables: object,
  graphql: string,
): Observable<FetchResult<WatchPostResponse>> => {
  const value = subscribe(
    gql`
    subscription watchPost($uuid: ID!) {
      postUpdated(uuid: $uuid) {
        ${graphql}
      }
    }
    `,
    variables,
  )

  return value
}

Type error given:

Type 'Observable<FetchResult<unknown>>' is not assignable to type 'Observable<FetchResult<WatchPostResponse>>'.
  Type 'FetchResult<unknown>' is not assignable to type 'FetchResult<WatchPostResponse>' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
    Type 'SingleExecutionResult<unknown, Record<string, any>, Record<string, any>>' is not assignable to type 'FetchResult<WatchPostResponse>' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
      Type 'SingleExecutionResult<unknown, Record<string, any>, Record<string, any>>' is not assignable to type 'SingleExecutionResult<WatchPostResponse, Record<string, any>, Record<string, any>>' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
        Type 'unknown' is not assignable to type 'WatchPostResponse'.ts(2322)

πŸ™ Actual behavior

Does not throw an error that return type of subscribe function is incompatible with expected function return type

πŸ™‚ Expected behavior

Should throw the same typing error like it with when assigning result to variable and returning that variable

Additional information about the issue

No response

@Andarist
Copy link
Contributor

This is working as intended. The one that typechecks OK benefits from return type inference. The subscribe call itself doesn't contain any positions from which your T could be inferred from.

So in the first example, the compiler falls back to other information that it can find in the surrounding context - and that's the return type of the outer function because the subscribe's result is "assigned" to it. The compiler is smart enough here to understand that subscribe is meant to return Observable<FetchResult<MaybeMasked<T>>> and that it's assigned to Observable<FetchResult<WatchPostResponse>>. So using the regular inference algorithm capabilities it infers from WatchPostResponse (source) into MaybeMasked<T> (target).

In the other example, you have "detached" it from that contextual information and therefore it falls back to the T's constraint and that's just unknown.

Note that your first example isn't particularly type-safe anyway. Given lack of inference candidates in the actual call, this is as safe as casting or as safe as assigning any to some known type. Given the nature of client-server communication and APIs, it's a reasonable tradeoff that many applications choose.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants