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

Intersection of two generic params to form a third is not recognized as equivalent #29399

Closed
ChuckJonas opened this issue Jan 13, 2019 · 3 comments
Labels
Question An issue which isn't directly actionable in code

Comments

@ChuckJonas
Copy link

TypeScript Version: 3.3.0-dev.201xxxxx

Search Terms:

Intersection of generics

Code

let connect = <R1,R2, R = R1&R2 >(mapState2Props: (state) => R1, mapDispatch2Props: (dispatch: any) => R2): R => {
  let state = {};
  let dispatch = () => { };
  return { ...mapState2Props(state), ...mapDispatch2Props(dispatch)}
}

Expected behavior:
I would expect that this would be valid because I have defined R = R1&R2 in the generic signature.

Actual behavior:

Get an error Type 'R1 & R2' is not assignable to type 'R'. on the return statement. I've also tried R extends R1&R2 and I get the same error.

Playground Link:

Link

Related Issues:

#5823

@ChuckJonas ChuckJonas changed the title Intercetion of Generics is not recognized as equivalent Intersection of two generic to form a third is not recognized as equivalent Jan 13, 2019
@ChuckJonas ChuckJonas changed the title Intersection of two generic to form a third is not recognized as equivalent Intersection of two generic params to form a third is not recognized as equivalent Jan 13, 2019
@jack-williams
Copy link
Collaborator

I believe the error is correct here. Yes R1 & R2 is assignable to the default of R, but R might be instantiated with a more specific type. Consider the following instantiation.

connect<{ x: number; }, { y: number; }, { x: number, y: number, z: number }>( ... );

This is a valid instantiation of R1, R2, and R, but R1 & R2 is not assignable to R because it is missing the z parameter.

In your example the R parameter is not actually required (though your real use-case may be more complicated). This works:

let connect = <R1,R2>(mapState2Props: (state) => R1, mapDispatch2Props: (dispatch: any) => R2): R1 & R2 => {
  let state = {};
  let dispatch = () => { };
  return { ...mapState2Props(state), ...mapDispatch2Props(dispatch)}
}

@ChuckJonas
Copy link
Author

hmm ya that's a good point. The goal here was to eventually get to where you could just specify the return type R and get type safety that mapState2Props & mapDispatch2Props combined would result in a valid R Object.

EG:

interface IComponentProps{
  foo: string;
  doFoo: ()=>void
}
let props: IComponentProps = connect<IComponentProps>(
  (state) => { foo: state.foo },
  (dispatch) => {doFoo: () => dispatch('FOO')}
)

However... I'm not sure if that is even possible... Seem like you would need to instead define R1 & R2 as some subset of R, but they are dependent on each other...

@nmain
Copy link

nmain commented Jan 14, 2019

Note that R could also be a completely unrelated type. Default type arguments in generics are not constraints.

connect<{ a: string }, { b: number }, { c: boolean[] }>(() => ({ a: "a" }), () => ({ b: 3 }))

@weswigham weswigham added the Question An issue which isn't directly actionable in code label Jan 14, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Question An issue which isn't directly actionable in code
Projects
None yet
Development

No branches or pull requests

5 participants