Skip to content

Wrong Pick inference starting from version 3.6.0 #33468

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
Siegrift opened this issue Sep 17, 2019 · 1 comment · Fixed by #33478
Closed

Wrong Pick inference starting from version 3.6.0 #33468

Siegrift opened this issue Sep 17, 2019 · 1 comment · Fixed by #33478
Assignees
Labels
Bug A bug in TypeScript

Comments

@Siegrift
Copy link

TypeScript Version: >3.6.0

Search Terms: Pick, Exclude, Omit

Code

type Set1<T, K1 extends keyof T> = T extends any[] ? T : Pick<T, Exclude<keyof T, K1>> & {
  [SK1 in K1]-?: Required<Pick<T, SK1>>;
}[K1];

type Set2<T, K1 extends keyof T, K2 extends keyof T[K1]> = T extends any[] ? T : Pick<T, Exclude<keyof T, K1>> & {
  [SK1 in K1]-?: Required<{
      [key in K1]: Set1<T[K1], K2>;
  }>;
}[K1];

declare function set<T, K1 extends keyof T>(source: T, path: [K1], value: T[K1]): Set1<T, K1>;

declare function set<T, K1 extends keyof T, K2 extends keyof T[K1]>(source: T, path: [K1, K2], value: T[K1][K2]): Set2<T, K1, K2>;


interface State {
  a: {
    b: string;
    c: number;
  };
  d: boolean;
}

const state: State = {
  a: {
    b: "",
    c: 0,
  },
  d: false,
};

const newState: State = set(state, ["a", 'b'], 'why'); // error

Expected behavior:
Type of the return value of set function should be assignable to State

Actual behavior:

Type 'Pick<State, "d"> & { a: (Pick<{ b: string; c: number; }, never> & Pick<{ b: string; c: number; }, "b">) | (Pick<{ b: string; c: number; }, never> & Pick<{ b: string; c: number; }, "c">); }' is not assignable to type 'State'.
  Types of property 'a' are incompatible.
    Type '(Pick<{ b: string; c: number; }, never> & Pick<{ b: string; c: number; }, "b">) | (Pick<{ b: string; c: number; }, never> & Pick<{ b: string; c: number; }, "c">)' is not assignable to type '{ b: string; c: number; }'.
      Property 'c' is missing in type 'Pick<{ b: string; c: number; }, never> & Pick<{ b: string; c: number; }, "b">' but required in type '{ b: string; c: number; }'.

Playground Link:
http://www.typescriptlang.org/play/#code/C4TwDgpgBAyhwEYA8AVANFA0gqEAewEAdgCYDOUA1hCAPYBmUKAfFALxO4HHlQCGREAG0AulAD8nAFxQACgEsAxpVQYAonkUAbAK4kISanUbosCZqwBkUAN4BYAFBQoQmNijyiZkQFpxMgCUIAEcdeQAnCBIkBWVVWGwLAG5HAF8hbBEUh0dQSFh4ACZ493xCUgojBiYMTEKuct4qkwyEEVYOFAaeCgFhMUkumViVUw1tPQNmmrMLKGt7Jxc3HE9vP0CQsMjoxed9lyMPL0yZOERUVpFawuZs51S7tKvsx31tPkioeh0iRWB5LQvGR4CUcGUelQaNUWAAKMi0HThRQQGSmMB8YAACxkVwwADc+LpUUwrgBKM7wZCmRKvBzvLSfaA-P4AoFQEHAMHdCpQ4wzOo8prQlqZZjwxHIknozE4lzYG7XKCE4loq4ZQoiCkFYDFGkIG5PHLGzyEcL0Pgo2DATHQPb8GT25wAIxkZGA4U8AHN7vtFDIiDoALbOiDhX2pX0kGTO2i0LQQATZVKORyKIHujk2whnbPQDj2viOxwHV1QABE5bQJb9MgADNWlqlG85o98iSDG5HUw500RM0QIAB3GB53O29gc+DwvMYITlvhVqAAcmdy6Vy6HWJAy7JSSAA
(Playground doesn't support >3.6.0 at this time, so there it works)

My observatioins:

  • If I remove the d property from state, the code works
  • If I remove the T extends any[] ? T : in Set1 and Set2 declarations the code works
  • For some reason, the code infers third generic argument of set (a.k.a K2 extends keyof T[K1]) as "b" | "c" which might be the issue here. However, I don't understand why it does it.
  • Tried looking at releases for 3.6.0 but I didn't see anything directly related.

Related Issues:
I tried looking for them, but it's hard to know what to search for... The closest to my problem seemed #28884, but it's not directly related (I think).

@weswigham
Copy link
Member

weswigham commented Sep 17, 2019

I have identified the issue, I think. I believe this regressed due to b75a90e - namely what happens is first, since we have no inferences, we contextually type K1 with the declared, uninstantiated signature. Then we have a type for K1, and when we're looking up the contextual type for the other array position, we go "ah, look, we have inferences now", and we try to instantiate the contextual type. This wouldn't be too bad, usually (since K2 wouldn't have an inference yet), except since we also try the returnMapper (which does exist), we end up instantiating K2 with unknown (since it has no inferences). unknown isn't assignable to the constraint, keyof T[K1], so we then take that instead, and instantiate that - out most recent inference for K1 at that time is "a" | "d" (which we made from the returnMapper during comparability checking with the first overload) - State["a" | "d"] is {b: string, c: number} | boolean, and keyof that is never (as boolean has no keys).

@weswigham weswigham added Bug A bug in TypeScript and removed Needs Investigation This issue needs a team member to investigate its status. labels Sep 17, 2019
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
3 participants