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

Pick and conditional update in React.setState #13359

Closed
kimamula opened this issue Jan 9, 2017 · 1 comment
Closed

Pick and conditional update in React.setState #13359

kimamula opened this issue Jan 9, 2017 · 1 comment
Labels
External Relates to another program, environment, or user action which we cannot control.

Comments

@kimamula
Copy link
Contributor

kimamula commented Jan 9, 2017

TypeScript Version: 2.1.4 / nightly (2.2.0-dev.20170108 )

Code

Pick is supposed to work for typing React.setState (#12793).
However, there is a problem if you want to conditionally change which attributes to update.

declare class Component<P, S> {
  setState<K extends keyof S>(f: (prevState: S, props: P) => Pick<S, K>, callback?: () => any): void;
}

declare const component: Component<{}, {
  foo: number;
  bar: string;
  someCondition: boolean;
}>;

component.setState(({ someCondition }) => {
  if (someCondition) {
    return { foo: 0 };
  } else {
    return { bar: '' };
  }
});

Expected behavior:
The code compiles successfully.

Actual behavior:
component.setState line in the above code emits an error.

error TS2345: Argument of type '({someCondition}: { foo: number; bar: string; someCondition: boolean; }) => { foo: number; } | { ...' is not assignable to parameter of type '(prevState: { foo: number; bar: string; someCondition: boolean; }, props: {}) => Pick<{ foo: numb...'.
  Type '{ foo: number; } | { bar: string; }' is not assignable to type 'Pick<{ foo: number; bar: string; someCondition: boolean; }, "foo" | "bar">'.
    Type '{ foo: number; }' is not assignable to type 'Pick<{ foo: number; bar: string; someCondition: boolean; }, "foo" | "bar">'.
      Property 'bar' is missing in type '{ foo: number; }'.

You have to write like this to avoid the error.

component.setState(({ foo, bar, someCondition }) => {
  if (someCondition) {
    return { foo: 0, bar };
  } else {
    return { foo, bar: '' };
  }
});
@RyanCavanaugh RyanCavanaugh added the Needs Investigation This issue needs a team member to investigate its status. label May 24, 2017
@RyanCavanaugh RyanCavanaugh added External Relates to another program, environment, or user action which we cannot control. and removed Needs Investigation This issue needs a team member to investigate its status. labels Sep 18, 2019
@RyanCavanaugh
Copy link
Member

Non-extensibly, you could write

declare class Component<P, S> {
  setState<K1 extends keyof S, K2 extends keyof S>(f: (prevState: S) => (Pick<S, K1> | Pick<S, K2>), callback?: () => any): void;
}

declare const component: Component<{}, {
  foo: number;
  bar: string;
  someCondition: boolean;
}>;

component.setState<"foo", "bar">(({ foo }) => {
    return !!true ?  { foo: 0 } : { bar: "" };
});

In general though, TS doesn't have a way to resolve this without using a unification-based inference algorithm.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
External Relates to another program, environment, or user action which we cannot control.
Projects
None yet
Development

No branches or pull requests

2 participants