Generic functions can keep array element types intact. If you return an element or a fallback of the same type parameter T, callers get the precise type back. Let inference do most of the work.
Use simple control flow. Avoid special cases for strings vs numbers—the type parameter will handle that.
function headOr<T>(xs: T[], d: T) {
return xs.length ? xs[0] : d;
}
Return the first element or a provided fallback.