Write a function that accepts every argument type except never. No parameter type can reject never, so the rejection has to move into the argument count.
The usual way to forbid a value is to narrow the parameter type. That move is useless here, because never is the bottom type: it is assignable to every type, including never itself. Whatever you write as the type of x, a never argument slips through.
So myFunc has to accept an argument of any type at all, void, unknown, any, never[], unions, object types, with exactly one exception. A value typed never must be a compile error at the call site.
declare const nothing: never
declare const name: string
myFunc(nothing) // expected to be a type error
myFunc(name) // expected to be fineWrite function myFunc so that it can accept any argument type except type
never.
View on GitHub: https://tsch.js.org/37398
Change the following code to make the test cases pass (no type check errors).
/* _____________ Your Code Here _____________ */
function myFunc(x: any) {
console.log(x)
}
/* _____________ Test Cases _____________ */
declare const x0: never
declare const x1: void
declare const x2: unknown
declare const x3: never[]
declare const x4: 1 | 2 | 3n
declare const x5: string
declare const x6: boolean | number
declare const x7: undefined | { a: 123 } | VoidFunction
declare const x8: string[]
declare const x9: {} | []
declare const x10: any
// @ts-expect-error
myFunc(x0)
myFunc(x1)
myFunc(x2)
myFunc(x3)
myFunc(x4)
myFunc(x5)
myFunc(x6)
myFunc(x7)
myFunc(x8)
myFunc(x9)
myFunc(Get access to all 200+ challenges, including every medium, hard, and extreme one.
Monthly subscription.
Cancel anytime + 30-day money-back guarantee.
The solution in full:
type RejectNever<T> = [T] extends [never] ? [never] : []
function myFunc<T>(x: T, ..._block: RejectNever<T>) {
console.log(x)
}One type parameter, one rest parameter, and the rejection happens on the rest parameter rather than on x.
The obvious first try is to compute the type of x from T:
[object Object]Inference works fine. Call it with a never value and TypeScript really does infer T = never, so the parameter type resolves to never. The call is still accepted, because the argument is also never, and never is assignable to never.
declare const nothing: never
naive(nothing) // no error, and the return type is neverThat is the lesson of this challenge. No type you can put on a parameter rejects a never argument, because every type accepts the bottom type. Any solution that only touches the type of x is dead on arrival.
What TypeScript does check independently of assignability is how many arguments a call supplies. A rest parameter typed with a tuple controls that count, and the tuple can depend on T:
// RejectNever<string> = [] → myFunc takes 1 argument
// RejectNever<never> = [never] → myFunc takes 2 argumentsT is inferred from x first, in the ordinary way, and only then does the rest parameter's tuple resolve. For every type other than never the tuple is empty and the signature is the one you want, myFunc(x: T).
For never, the tuple has one required element, so calling with a single argument fails:
// @ts-expect-error: Expected 2 arguments, but got 1. ts(2554)
myFunc(nothing)There is no way to repair that call either. The missing parameter has type never, and no expression has type never, so the second argument cannot be produced. The rest parameter is pure type-level machinery: in a passing call it is empty, so nothing extra is ever passed at runtime.
T is wrapped in a tupleRejectNever<T> asks [T] extends [never], not T extends never. A conditional type with a bare type parameter on the left distributes over unions, and never is the empty union. Distributing over zero union members produces zero results, so the whole conditional collapses to never before either branch is picked.
type Naked<T> = T extends never ? [never] : []
type A = Naked<never> // never, not [never]
type B = Naked<string> // []Wrapping both sides in a one-element tuple turns off distribution and asks the question literally: is T itself never. The naked version happens to reject the test call as well, but for an accidental reason, and the error it reports is Argument of type '[]' is not assignable to parameter of type 'never', which tells a reader nothing. The bracketed form keeps the resolved type a real tuple and produces the honest arity error.
void, unknown and any: each infers as itself, and [any] extends [never] is false, so any goes through the permissive branch like everything else.never[]: an array whose element type is never is still a perfectly inhabited type at the type level, [never[]] extends [never] is false, and the call is accepted.1 | 2 | 3n, boolean | number and undefined | { a: 123 } | VoidFunction: T is inferred as the whole union, and because the brackets block distribution the union is tested once rather than member by member.{} | []: no structural constraint on T at all, so object and array shapes pass unchanged.never: the only rejected case, and it fails with a counting error rather than an assignability one.This challenge is originally from here.
Track your progress through 200+ hands-on challenges. Free, sign in with GitHub.
Or start solving right away: explore all TypeScript challenges