The core of the question would be given a tuple type T1 and a second tuple type T2 which extends Partial<T1>, how could I get a third type which represents the sequence of items that are in T1 but not in T2.
Assumption I am currently making the assumption that given a tuple T, a tuple that may contain the first item in T, or the first and second items of T, or the first 3 items … etc may be represented by Partial<T>. If this is not correct please explain how this could be represented.
Context I would like to create a function f that takes a generic tuple of type T and an argument a of type T2 that extends Partial<T>, the function would then return another function with an argument of type T3 that represents a tuple that should consist of the items that when added to the first tuple argument should recreate the initial tuple. Such that tuple types T2 + T3 should equal type T, where + represents concatenating the tuples.
This is what I have so far
type R<T extends any[], T2 extends Partial<T>> = never; // this would represent the type I'm looking for
const f =
<T extends any[]>() =>
<T2 extends Partial<T>>(t1: T2) =>
(t2: R<T, T2>): T =>
[...t1, ...t2];
Note here there are three functions as the first one is just to provide the generic type T for use in the following two functions. If I were to call f as follows
const f1 = f<[1, 2, 3, 4, 5]>()([1,2])
f1([3,4,5])
I would like for [3,4,5] to be the expected type if possible.
Thanks for any help, here’s a playground link with the above.
>Solution :
You can use conditional type inference with variadic tuple types to "subtract" tuples:
type R<T extends any[], T2 extends Partial<T>> =
T extends [...T2, ...infer D] ? D : never
We can verify that this works as desired:
const f =
<T extends any[]>() =>
<T2 extends Partial<T>>(t1: T2) =>
(t2: R<T, T2>): T =>
[...t1, ...t2] as T;
const f1 = f<[1, 2, 3, 4, 5]>()([1, 2])
// const f1: (t2: [3, 4, 5]) => [1, 2, 3, 4, 5]
f1([3, 4, 5]); // okay
Note that in the implementation I just asserted that the return type is correct to avoid dealing with compiler errors. The reason the compiler issues an error there is primarily because it cannot reason about generic conditional types like [...T2, ...R<T, T2>] for arbitrary T and T2. All it sees is that [...t1, ...t2] is of type (T2[number] | R<T, T2>[number])[], which is correct as far as it goes, but not specific enough to guarantee that it is compatible with T.