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

TypeScript errors with --strictFunctionTypes flag. #391

Closed
a-shtubov opened this issue Jan 9, 2019 · 8 comments
Closed

TypeScript errors with --strictFunctionTypes flag. #391

a-shtubov opened this issue Jan 9, 2019 · 8 comments
Milestone

Comments

@a-shtubov
Copy link

reselect: 4.0.0
typescript: 3.2.2

The following code produces TS error.

// tsconfig.json
{
  "compilerOptions": {
    "strictFunctionTypes": true
  }
}
// app.ts
import { createStructuredSelector } from 'reselect';

type GlobalState = {
  foo: string;
  bar: number;
};

const selectFoo = (state: GlobalState) => state.foo;
const selectBar = (state: GlobalState) => state.bar;

const injectState = createStructuredSelector({
  foo: selectFoo, // error
  bar: selectBar, // error
});

Error text

Type '(state: GlobalState) => string' is not assignable to type 'ParametricSelector<{}, {}, string>'.
  Types of parameters 'state' and 'state' are incompatible.
    Type '{}' is missing the following properties from type 'GlobalState': foo, bar [2322]

With strictFunctionTypes disabled, everything works fine. What is the correct way of using createStructuredSelector in TypeScript withstrictFunctionTypes option?

@OliverJAsh
Copy link

I also just ran into this. Filed a TypeScript issue. See the issue for details on workaround: microsoft/TypeScript#29479.

@markerikson
Copy link
Contributor

Is this still an issue with TS 4.x?

@a-shtubov
Copy link
Author

Yes. Just tested this example with TS 4.1.2 and reselect 4.0.0

@markerikson
Copy link
Contributor

Okay. Do the changes in #480 do anything to improve the situation?

@a-shtubov
Copy link
Author

No. Although the error message has changed.

Type '(state: GlobalState) => string' is not assignable to type '(state: unknown) => string'.
  Types of parameters 'state' and 'state' are incompatible.
    Type '{}' is missing the following properties from type 'GlobalState': foo, bar

@eXamadeus
Copy link
Contributor

eXamadeus commented Feb 8, 2021

So it looks like the original typing of createSturcturedSelector was meant to take in type params to correctly generate the output.

You basically want this:

import { createStructuredSelector } from 'reselect';

type GlobalState = {
  foo: string;
  bar: number;
};

const selectFoo = (state: GlobalState) => state.foo;
const selectBar = (state: GlobalState) => state.bar;

// <Input, Output>
const injectState = createStructuredSelector<GlobalState, { foo: string, bar: number }>({
  foo: selectFoo, 
  bar: selectBar,
});

where Input is GlobalState and Output is whatever the structured selector will map too (in this case, it's the same as GlobalState but it could be different).

Basically for this typing to work, you need to provide the "input" and "output" types. @markerikson we should note this as something we might be able to improve in #484. Not sure how easy it will be though.

@eXamadeus
Copy link
Contributor

eXamadeus commented Feb 8, 2021

There is a minor improvement we can do to make this easier in 4.x which is to provide a default for the "output" type, so if people just want to "map" a state over, they can do that. Granted, I'm not wholly convinced this adds much to the experience.

@markerikson I'll add a test for this in #486 like this:

function testStructuredSelectorTypeParams() {
  type GlobalState = {
    foo: string;
    bar: number;
  };

  const selectFoo = (state: GlobalState) => state.foo;
  const selectBar = (state: GlobalState) => state.bar;

  // Output state should be the same as input, if not provided
  // @ts-expect-error
  createStructuredSelector<GlobalState>({
    foo: selectFoo,
    // bar: selectBar,
    // ^^^ because this is missing, an error is thrown
  });

  // This works
  createStructuredSelector<GlobalState>({
    foo: selectFoo,
    bar: selectBar,
  });

  // So does this
  createStructuredSelector<GlobalState, Omit<GlobalState, 'bar'>>({
    foo: selectFoo,
  });
}

It's easy enough to revert if you think it's overkill.

@eXamadeus eXamadeus mentioned this issue Feb 8, 2021
3 tasks
eXamadeus added a commit to eXamadeus/reselect that referenced this issue Feb 9, 2021
@markerikson markerikson added this to the 4.1 milestone Oct 17, 2021
@markerikson
Copy link
Contributor

Should be fixed by #486 .

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

No branches or pull requests

4 participants