type UnionToIntersection<T> = (T extends T ? (args: T) => any : never) extends (args: infer P) => any ? P : never;
type UnionLast<T> = (UnionToIntersection<T extends T ? () => T : never>) extends () => infer R ? R : never;
type UnionToTuple<T> = [T] extends [never] ? [] : [UnionLast<T>, ...UnionToTuple<Exclude<T, UnionLast<T>>>];
type IsUnion<T, U = T> = U extends T ? [Exclude<T, U>] extends [never] ? false : true : never;
type LongestCommonPrefix<T extends string[], P extends string = ''> = T[number] extends `${infer F}${infer R}` ?
IsUnion<F> extends false ? UnionToTuple<R> extends infer UR extends string[] ? LongestCommonPrefix<UR, `${P}${F}`> :
P : P : P;
Solution by E-uler #35365
type IsSingle<V, U = V> = V extends unknown ? [U] extends [V] ? true : false : never;
// Longest Common Prefix for string literals union
type LCP<S, R extends string = ''> = [S] extends [`${infer F}${infer T}`]
? true extends IsSingle<F>
? LCP<T, `${R}${F}`>
: R
: R;
type LongestCommonPrefix<T extends string[]> = LCP<T[number]>;
Solution by alexandroppolus #35292
type LongestCommonPrefix<T extends string[], P extends string = ''>
= T extends [`${P}${infer Next}${any}`, ...any]
? T extends `${P}${Next}${any}`[]
? LongestCommonPrefix<T, `${P}${Next}`>
: P // the longest
: P // T is empty or end of T[0]
type LongestCommonPrefix<T extends string[], P extends string = ''>
= T extends `${P}${infer Next}${any}`[]
? {} extends {[P in Next as Exclude<Next, P>]: 1}
? LongestCommonPrefix<T, `${P}${Next}`>
: P // Next is union, return P
: P // T is empty or all empty string
Solution by teamchong #35251