Reputation: 469
I have interface with list of optional properties.
export interface OptionalIds {
entityA_Id?: number;
entityB_Id?: number;
entityC_Id?: number;
}
And I have a requirement that EXACTLY one of them MUST be defined. Something like that:
export interface RequiredBId {
entityA_Id?: undefined;
entityB_Id: number;
entityC_Id?: undefined;
}
export interface RequiredCId {
entityA_Id?: undefined;
entityB_Id?: undefined;
entityC_Id: number;
}
export interface OptionalIds {
entityA_Id?: number;
entityB_Id?: number;
entityC_Id?: number;
}
export type RestrictedOptionalIds = OptionalIds & (RequiredAId | RequiredBId | RequiredCId)
The question is: Is there other way to achieve described behaviour without weird constructions?
Upvotes: 7
Views: 824
Reputation: 2289
Thanks to this post for RequireOnlyOne
: https://stackoverflow.com/a/49725198/4529555
type RequireOnlyOne<T, Keys extends keyof T = keyof T> =
Pick<T, Exclude<keyof T, Keys>>
& {
[K in Keys]-?:
Required<Pick<T, K>>
& Partial<Record<Exclude<Keys, K>, undefined>>
}[Keys]
export interface OptionalIds {
entityA_Id?: number;
entityB_Id?: number;
entityC_Id?: number;
}
const exampleA: RequireOnlyOne<OptionalIds> = {
entityA_Id: 1
}
const exampleB: RequireOnlyOne<OptionalIds> = {
entityB_Id: 1
}
const exampleC: RequireOnlyOne<OptionalIds> = {
entityC_Id: 1
}
// Error
const exampleMultiple: RequireOnlyOne<OptionalIds> = {
entityA_Id: 1,
entityB_Id: 2,
}
// Error: {} not assignable to RequireAtLeastOne<OptionalIds, keyof OptionalIds>
const exampleTsError: RequireOnlyOne<OptionalIds> = {
}
Thanks to this post for RequireAtLeastOne
: https://stackoverflow.com/a/49725198/4529555
type RequireAtLeastOne<T, Keys extends keyof T = keyof T> =
Pick<T, Exclude<keyof T, Keys>>
& {
[K in Keys]-?: Required<Pick<T, K>> & Partial<Pick<T, Exclude<Keys, K>>>
}[Keys]
export interface OptionalIds {
entityA_Id?: number;
entityB_Id?: number;
entityC_Id?: number;
}
const exampleA: RequireAtLeastOne<OptionalIds> = {
entityA_Id: 1
}
const exampleB: RequireAtLeastOne<OptionalIds> = {
entityB_Id: 1
}
const exampleC: RequireAtLeastOne<OptionalIds> = {
entityC_Id: 1
}
const exampleMultiple: RequireAtLeastOne<OptionalIds> = {
entityA_Id: 1,
entityB_Id: 2
}
// Error: {} not assignable to RequireAtLeastOne<OptionalIds, keyof OptionalIds>
const exampleTsError: RequireAtLeastOne<OptionalIds> = {
}
Upvotes: 2