If there are additional members in the `IMyInterface` that need to be preserved, I suggest a more generalized approach based on the @Catalyst response.
type EachExpanded<T> = {
[key in keyof T]: { [subKey in key]: T[key]; }
};
type FixedSubset<T, U> = Pick<T, Exclude<keyof T, U>>;
type AtLeastSubset<T, U> = Pick<T, Extract<keyof T, U>>;
type AtLeaseOne<T, U> = FixedSubset<T, U> & EachExpanded<AtLeastSubset<T, U>>[keyof AtLeastSubset<T, U>];
const example1: AtLeaseOne<{ a: number; b: number; c: string; }, 'a' | 'b'> =
{ a: 3, b: 4, c: '4' } // valid
const example2: AtLeaseOne<{ a: number; b: number; c: string; }, 'a' | 'b'> =
{ a: 1, c: '1' } // valid
const example3: AtLeaseOne<{ a: number; b: number; c: string; }, 'a' | 'b'> =
{ b: 2, c: '2' } // valid
const example4: AtLeaseOne<{ a: number; b: number; c: string; }, 'a' | 'b'> =
{ c: '3' } // invalid
Note that this solution utilizes the recently introduced `Exclude` and `Extract` keywords in TypeScript version 2.8.
In this code snippet, we assume type `T` represents the original type or interface, while `U` signifies the set of keys where at least one must be present.
The implementation involves creating a type that excludes properties requiring implementation from the original type `T`. This is achieved through `Pick<T, Exclude<keyof T, U>>`, which includes everything except items in `U`.
Additionally, another type is constructed containing elements where at least one must be present: `Pick<T, Extract<keyof T, U>>`.
The `EachExpanded` type defines the structure for each special set under the same key. For instance, if keys `'a'` and `'b'` need to become conditionally optional, `EachExpanded` creates:
{
a: { a: number; };
b: { b: number; };
}
This composite type is then combined using an intersection operator to enforce the presence of at least one item.
Ultimately, for the given example, the resulting type would look like this:
{ c: string; } & ({ a: number; } | { b: number; })