You can just use variadic tuple types (TS 4.0) instead of adding an additional type abstraction:
type T3 = ["g", "h", "i"]
type Concatenated = [...T1, ...T2, ...T3] // ["a", "b", ..., "h", "i"]
If there is still need for a separate Concat type, following will make use of TS 4.1 recursive conditional types and is more strict than Aleksey's answer:
// separate initial and recursive type constructor
type Concat<T extends unknown[][]> = ConcatRec<T>
type ConcatRec<T extends unknown[]> = T extends [infer I, ...infer R] ?
I extends unknown[] ? [...I, ...ConcatRec<R>] : never
: T
type R1 = Concat<[T1, T2, T3]>; // ["a", "b", "c", ... , "i"]
type R2 = Concat<["foo"]>; // now errors properly
For example you can combine this type with Array.prototype.concat:
function concat<T extends [unknown, ...unknown[]][]>(...t: T): Concat<T> {
return [].concat(...t as any) as any
}
const result = concat([1, "a", 3], [true, 5, new Date()])
// const res: [number, string, number, boolean, number, Date]
Live code sample on Playground