I have a union of two types, one of which is an empty obj.
type U = {} | { a: number } // | { b: string } | { c: boolean } ....
I would like to exclude the empty object from the union however Exclude
is no help
type A = Exclude<U, {}>
// A = never
I tried using as const
but it's the same result
const empty = {} as const
type Empty = typeof empty
type U = Empty | { a: number }
type A = Exclude<U, Empty>
//type A = never
The extra Irony is that excluding the other properties is straightforward
type B = Exclude<U, { a: number }>
// type B = {}
TS Playground
So is it possible to exclude an empty interface from other interfaces in a union?
Answering my own question..
If you use AtLeastOne
from @lukasgeiter answer here: Exclude empty object from Partial type
you can do the following:
type AtLeastOne<T, U = {[K in keyof T]: Pick<T, K> }> = Partial<T> & U[keyof U];
type ExcludeEmpty<T> = T extends AtLeastOne<T> ? T : never;
type U = {} | { a: number } | { b: string }
type Foo = ExcludeEmpty<U> // { a: number } | { b: string }
TSplayground
From the docs for conditional typing here, you can actually assign types based on some condition.
T extends U ? X : Y
So for the above question, what you could do is make use of keyof
keyword that is used to extract keys from objects. When there is no any keys found the type is never so, we can check if the keyof object
extends never or not i.e.
keyof K extends never
So combing conditional typing below;
const empty = {} as const
type Empty = typeof empty
type NoEmpty<K> = keyof K extends never ? never : K;
type C = NoEmpty<Empty>;
type U = NoEmpty<Empty> | NoEmpty<{ a: number }>
You can finally see that type of the U to be non empty object i.e excluding empty object. Check this playground
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With