I'd like to type an object that can contain any property, but if it has any of the properties a
, b
or c
, it must contain all of the properies a
, b
and c
.
In other words: the object can contain any property, but if it contains a
, b
or c
it must contain a
, b
and c
.
let something: Something;
// these should be valid:
something = { a: 1, b: 1, c:, 1 };
something = { a: 1, b: 1, c:, 1, custom: 'custom' };
something = { custom: 'custom' };
// these should be invalid:
something = { a: 1 };
something = { a: 1, b: 1 };
something = { a: 1, c: 1 };
something = { b: 1 };
something = { a: 1, custom: 'custom' };
How can this be achieved?
Creating a union type with an object seems correct, but doesn't work:
interface RequiredProps {
a: number;
b: number;
c: number;
}
type Something = RequiredProps | {};
You can create a union type, between a type that allows a
, b
, c
and an indexer, and one that forces a
, b
, and c
if present to have type never
.
type Something = ({ a: number, b: number, c: number } & { [n: string]: any }) |
( { a?: never, b?: never, c?: never } &{ [n: string]: any });
If you have functions that return never
it may be possible to assign the properties but for most practical cases this should work.
We could even create a generic type for this situation:
type RequireAll<TOptional, TRequired> = (TOptional & TRequired) | (Partial<Record<keyof TOptional, never>> & TRequired)
type Something = RequireAll<{a: number, b: number, c:number}, {[n:string]:any}>
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