I have a fully working example located here:
The code works, but TypeScript is complaining. Unfortunately I have run out of ideas of how I can make the code properly type safe. I have scoped the web for numerous examples however I haven’t found one that has my use case unfortunately.
I would like the code to accept a generic type and an array of said generic type and for the code to understand that. also at the same time providing those values to child components via the context provider.
Some of the errors.
1 (line 35):
Type '((activeIdOrIds: T[]) => void) | ((activeIdOrIds: T) => void)' is not assignable to type '((activeIdOrIds: Id[]) => void) | ((activeIdOrIds: Id) => void)'.
Type '(activeIdOrIds: T[]) => void' is not assignable to type '((activeIdOrIds: Id[]) => void) | ((activeIdOrIds: Id) => void)'.
Type '(activeIdOrIds: T[]) => void' is not assignable to type '(activeIdOrIds: Id[]) => void'.
Types of parameters 'activeIdOrIds' and 'activeIdOrIds' are incompatible.
Type 'Id[]' is not assignable to type 'T[]'.
Type 'Id' is not assignable to type 'T'.
'Id' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Id'.
Type 'null' is not assignable to type 'T'.
'null' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Id'.ts(2322)
2 (line 66):
Type 'Id[]' is not assignable to type 'T[]'.
Type 'Id' is not assignable to type 'T'.
'Id' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Id'.
Type 'null' is not assignable to type 'T'.
'null' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'Id'.ts(2322)
3 (line 68):
Argument of type 'T[]' is not assignable to parameter of type 'Id[] & Id'.
Type 'T[]' is not assignable to type 'Id[] & number'.
Type 'T[]' is not assignable to type 'number'.ts(2345)
As you see my generics seem to be at issue with the constraints of Id.
2
Answers
The reason is that your specify type itself is too absurd, see the following example:
now you initialize
createTestyContext
without parameterT
initially it will beunknown
but when rendering specifying forprops
typeT
is being specified asT extends Id
so these 2 types can’t matchT
is definitely notT extends Id
now you have to specify
T
asT
orT
asany
or like this:or like this:
So, the short explanation for why typescript is yelling is because your types don’t respect generics covariance. This is because Typescript is concerned with statically checking that types will always be compatible, no matter what you feed it.
A quick example to show how your types could fail (using animals instead of ids for simplicity)
Say you have the following
Typescript can’t tell how an object is going to get used at runtime, so needs to verify there is no edge case where the types used can be violated. So it has to check strictly that when assigning different types, that one is a complete subset of another.
As for your code itself, I would refactor it down to only use the type
OneOrMore = T | T[]
. The case of single of multiple is handled by[id]
, and you know if multiple are allowed based on if it is an array or not. Everything else can be safely extrapolated from there.