I have a function, and it can operate on any type T
. The only constraint is "if T
is an object, it can't be an object which can potentially be empty".
I tried this:
declare function func<T>(o: T extends Record<string, never> ? never : T): void;
func("string") // passes, as expected
func({}) // fails, as expected
func<{x?: number}>({}) // passes, noooo
Also this:
type EmptyObj = Omit<{x: number}, 'x'>
declare function func2<T>(o: T extends EmptyObj ? never : T): void;
func2("HI") // fails
func2({}). // fails
func2<{x?: number}>({x: 1}) // fails, as expected
func2<{x: number}>({x: 1}) // fails
func2
is probably running into the the fact {}
is the top type, so everything extends it. Is typescript capable of describing what I need it to?
Here's one approach:
declare function func<T extends ({} extends T ? never : unknown)>(o: T): void;
This is a recursive constraint on the type parameter T
(such self-referential constraints are known as F-bounded quantification and allow more expressive constraints where they are allowed) which amounts to "allow T
if and only if the empty object type {}
is not assignable to T
". If you can assign an empty object to T
, then T
should be rejected; if you cannot, then T
should be accepted.
This is implemented by constraining T
to the conditional type {} extends T ? never : unknown
. If {} extends T
is true, then {}
is assignable to T
, and so the conditional type resolves to never
, and so the constraint is T extends never
which will not be met, and you'll get an error. On the other hand, if {} extends T
is false, then {}
is not assignable to T
, and so the conditional type resolves to unknown
, and so the constraint is T extends unknown
, which will essentially always be met, and you won't get an error.
Let's test it:
func("string") // passes
func({}) // fails
func<{ x?: number }>({}) // fails
Looks good. Since {} extends string
is false, you can call func("string")
. But since {} extends {}
is true and since {} extends { x?: number}
is true, then you cannot call func({})
or func<{x?: number}>({})
.