import type {KeysOfUnion} from './keys-of-union';

/**
Omits keys from a type, distributing the operation over a union.

TypeScript's `Omit` doesn't distribute over unions, leading to the erasure of unique properties from union members when omitting keys. This creates a type that only retains properties common to all union members, making it impossible to access member-specific properties after the Omit. Essentially, using `Omit` on a union type merges the types into a less specific one, hindering type narrowing and property access based on discriminants. This type solves that.

Example:

```
type A = {
	discriminant: 'A';
	foo: string;
	a: number;
};

type B = {
	discriminant: 'B';
	foo: string;
	b: string;
};

type Union = A | B;

type OmittedUnion = Omit<Union, 'foo'>;
//=> {discriminant: 'A' | 'B'}

const omittedUnion: OmittedUnion = createOmittedUnion();

if (omittedUnion.discriminant === 'A') {
	// We would like to narrow `omittedUnion`'s type
	// to `A` here, but we can't because `Omit`
	// doesn't distribute over unions.

	omittedUnion.a;
 	//=> Error: `a` is not a property of `{discriminant: 'A' | 'B'}`
}
```

While `Except` solves this problem, it restricts the keys you can omit to the ones that are present in **ALL** union members, where `DistributedOmit` allows you to omit keys that are present in **ANY** union member.

@example
```
type A = {
	discriminant: 'A';
	foo: string;
	a: number;
};

type B = {
	discriminant: 'B';
	foo: string;
	bar: string;
	b: string;
};

type C = {
	discriminant: 'C';
	bar: string;
	c: boolean;
};

// Notice that `foo` exists in `A` and `B`, but not in `C`, and
// `bar` exists in `B` and `C`, but not in `A`.

type Union = A | B | C;

type OmittedUnion = DistributedOmit<Union, 'foo' | 'bar'>;

const omittedUnion: OmittedUnion = createOmittedUnion();

if (omittedUnion.discriminant === 'A') {
	omittedUnion.a;
 	//=> OK

	omittedUnion.foo;
 	//=> Error: `foo` is not a property of `{discriminant: 'A'; a: string}`

	omittedUnion.bar;
 	//=> Error: `bar` is not a property of `{discriminant: 'A'; a: string}`
}
```

@category Object
*/
export type DistributedOmit<ObjectType, KeyType extends KeysOfUnion<ObjectType>> =
	ObjectType extends unknown
		? Omit<ObjectType, KeyType>
		: never;
