1

In the following, imho, at least the error message indicates TS is incorrectly using an intersection for member access with a union:

type O = { i: [string]; j: [boolean] };
type HOF = <U extends keyof O>(a: (...args: O[U]) => void) => void;

const f: HOF = (a: (...args: any[]) => void) => {};

Playground link

I can't understand a step in the error (numbering mine):

1: Type '(a: (...args: any[]) => {}) => void' is not assignable to type 'HOF'.
  2: Types of parameters 'a' and 'a' are incompatible.
    3: Types of parameters 'args' and 'args' are incompatible.
      4: Type 'any[]' is not assignable to type 'O[U]'.
        5: Type 'any[]' is not assignable to type 'never'.
          6: The intersection '[string] & [boolean]' was reduced to 'never' because property '0' has conflicting types in some constituents.(2322)

How did we get from (4) O[U] to (6) [string] & [boolean]?

Doofus
  • 952
  • 4
  • 19
  • 1
    As far as I can understand, `O` extends `keyof U`, so `O` is `"i" | "j"`, since `i` and `j` are the attributes of `O` and thus they are its keys as well. `O["i"]` will return a value of type `[string]`, while `O["j"]` will return a value of type `[boolean]`, so I can imagine that `O[U]` will yield a value that can either be a `[string]` or a `[boolean]`. For that purpose, TypeScript will create a type that will hold the members that `[string]` and `[boolean]` have in common: the intersection `[string] & [boolean]`. (Perhaps this is not quite correctly formulated. Sorry in advance.) – Bart Hofland Jul 11 '22 at 17:38
  • if you want multiple param of type `O[U]` the args in HOF definition should be defined as `O[U][]`, since it will only be a tuple otherwise, unless im mistaken. https://www.typescriptlang.org/play?#code/C4TwDgpgBA8lC8UDeUCWAuKBtAzsATqgHYDmAugNxQBWmWARgPaMA2EAhkWVAL4UCwAKFCQoACRgAxBFAA8AVSgQAHsAhEAJjigBrCCEYAzWAD4AFO0xmAdLfb4SOTDCzyyWMgEoEJqADdGVA1veF8AoIFBIQBjRiI8KENMCWlECytba3tHTE4QDxCwwOCfZB4hIA it works with said change. – Dean Jul 11 '22 at 17:48
  • @BartHofland you probably meant "`U` extends `keyof O`, so `U` [...]". There is a type for "either be a `[string]` or a `[boolean]`", which is `[string] | [boolean]`, the one i would expect here. E.g. `O[keyof O]` evaluates to that (under normal circumstances). Also no, i don't want an array. To elaborate, i was trying to bypass [my previous question's problem](https://stackoverflow.com/questions/72939064/contextual-type-of-dependent-parameter) by switching to generics (which sometimes works, but has its limits). The intention was to generate "overloads" with contextual typing. – Doofus Jul 11 '22 at 18:12
  • 1
    Contravariance turns unions into intersections. Look at [this code](https://tsplay.dev/mpnYpw). I should be allowed to call `f<"i">(s => s.toUpperCase())` without worry, if `f` is of type `HOF`. But your `f` has a typing such that you could call its callback with any parameters you want, like `a(123)`. That's not good, and leads to a runtime error. The only safe function input for `f`'s callback is `[string] & [boolean]`, which is `never` and not `any[]`. Does that address your question fully? If so I could write up an answer; if not, what am I missing? – jcalz Jul 12 '22 at 02:08
  • Thanks, yes, it solves the problem. I had a brain knot, where i failed to notice, that the entire thing can be instantiated for every possible generic parameter, meaning it behaves like a union of functions, and not a union inside the parameter. It's probably best for me to consider a few of these cases, and sleep over it, so i won't do that mistake again :) – Doofus Jul 12 '22 at 13:56

0 Answers0