Type a helper that accepts one item or an array of them and returns an array
toArray normalizes a value that may arrive either as a single item or as an array of items.
Requirements:
call site, no any, no union in the result;
toArray(user)andtoArray(users)must both be typedUser[]— no type argument at the- the input array must be returned as is (do not copy it);
- a single item must come back wrapped in a one-element array.
export function toArray(input) {
// your code here
}
Write the implementation.
Declare function toArray<T>(input: T | T[]): T[]. The compiler infers T from either branch — toArray(user) and toArray(users) both give User[]. Inside, Array.isArray(input) narrows the union, and no type argument is needed.
- ✗Believing inference cannot resolve
Tthrough aT | T[]parameter - ✗Returning
T[] | T[][]because the union was not narrowed inside the body - ✗Reaching for overloads where a single generic union parameter is enough
- →What does
toArray(null)infer forT, and how would you exclude it? - →How would the signature change if the input could be
readonly T[]?
One generic parameter and a union on the input: inference reads both branches and lands on the same T.
export function toArray<T>(input: T | T[]): T[] {
return Array.isArray(input) ? input : [input];
}
const a = toArray(user); // User[]
const b = toArray(users); // User[]
How the compiler infers T. Candidates are collected from both branches of T | T[]: for user: User the first branch matches and yields T = User; for users: User[] the second matches and also yields T = User. No type argument is needed at the call site.
Array.isArray(input) is a built-in type guard: in the true branch input narrows to T[], in the false branch to T. Without that narrowing input would stay T | T[], and [input] would produce (T | T[])[].