Skip to content

React .tsx with Union Type Props reports error. But works in pure TS #26004

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
benneq opened this issue Jul 27, 2018 · 3 comments
Closed

React .tsx with Union Type Props reports error. But works in pure TS #26004

benneq opened this issue Jul 27, 2018 · 3 comments
Assignees
Labels
Bug A bug in TypeScript

Comments

@benneq
Copy link

benneq commented Jul 27, 2018

TypeScript Version: 2.9.2
(also tried typescript@3.1.0-dev.20180727 )

Search Terms: React Tsx Union Generics

Code

interface PS {
    multi: false
    value: string | undefined
    onChange: (selection: string | undefined) => void
}

interface PM {
    multi: true
    value: string[]
    onChange: (selection: string[]) => void
}

export function ComponentWithUnion(props: PM | PS) {
    return "foo";
}

// Usage with React tsx
export function HereIsTheError() {
    return (
        <ComponentWithUnion
            multi={false}
            value={'s'}
            onChange={val => console.log(val)} // <- this throws an error
        />
    );
}

// Usage with pure TypeScript
ComponentWithUnion({
    multi: false,
    value: 's',
    onChange: val => console.log(val) // <- this works fine
})

Expected behavior:
Both should infer the correct type and don't throw an error.

Actual behavior:
Throw's the following error:

Parameter 'val' implicitly has an 'any' type

Playground Link:
https://codesandbox.io/s/w07omnr85k
In file index.ts at line 25 you can see that val is of type any. But in line 34 it correctly displays type string.

Related Issues:
None

@mattmccutchen
Copy link
Contributor

mattmccutchen commented Jul 27, 2018

Looks like the problem is that each JSX attribute is contextually typed independently. If you use a spread attribute, it does work:

export function HereIsTheError() {
  return (
    <ComponentWithUnion {...{
      multi: false,
      value: "s",
      onChange: val => console.log(val)
    }}/>
  );
}

Could the compiler just treat all the JSX attributes as if they were written in a single object literal?

@pelotom
Copy link

pelotom commented Aug 14, 2018

This affects overloads as well as unions:

import * as React from 'react';

declare function Case1(arg:
  | { tag: 'foo'; f(n: number): void }
  | { tag: 'bar'; f(s: string): void }
): JSX.Element;

declare function Case2(x: { tag: 'foo'; f(n: number): void }): JSX.Element;
declare function Case2(x: { tag: 'bar'; f(s: string): void }): JSX.Element;

Case1({ tag: 'foo', f: n => {} });
<Case1 tag='foo' f={n => {}} />; // Parameter 'n' implicitly has an 'any' type.

Case2({ tag: 'foo', f: n => {} });
<Case2 tag='foo' f={n => {}} />; // Parameter 'n' implicitly has an 'any' type.

@pelotom
Copy link

pelotom commented Aug 14, 2018

Also here's a case where wrapping the props in a JSX spread doesn't help:

import * as React from 'react';

interface ExampleProps<A> {
  initial: A
  transition(x: A): A
}

declare const Example: {
  (props: { string: true } & ExampleProps<string>): JSX.Element;
  <A>(props: ExampleProps<A>): JSX.Element;
}

<Example initial={3} transition={x => x + 1} />; // x: any
<Example {...{initial: 3, transition: x => x + 1}} />; // x: any
Example({ initial: 3, transition: x => x + 1 }); // x: number

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

No branches or pull requests

5 participants