Below two code blocks are doing same job and the only difference is below second code block uses variadic tuple. Check the below links for more details.
type CompareLength<Left extends any[], Right extends any[]> =
Left['length'] extends Right['length'] ? 'equal' :
Left extends [] ? 'shorterLeft' :
Right extends [] ? 'shorterRight' :
((..._: Left) => any) extends ((_: any, ..._1: infer LeftRest) => any) ?
((..._: Right) => any) extends ((_: any, ..._1: infer RightRest) => any) ?
CompareLength<LeftRest, RightRest> :
never :
never;
type T1 = CompareLength<[firstName: string], [lastName: string]>;
type T2 = CompareLength<[firstName: string], [lastName: string, age: number]>;
type T3 = CompareLength<[firstName: string, age: number], [lastName: string]>;
type CompareLength<Left extends any[], Right extends any[]> =
Left['length'] extends Right['length'] ? 'equal' :
Left extends [] ? 'shorterLeft' :
Right extends [] ? 'shorterRight' :
Left extends [any, ...infer L] ?
Right extends [any, ...infer R] ?
CompareLength<L, R> :
never :
never;
type T1 = CompareLength<[firstName: string], [lastName: string]>;
type T2 = CompareLength<[firstName: string], [lastName: string, age: number]>;
type T3 = CompareLength<[firstName: string, age: number], [lastName: string]>;