Home > OS >  one field is required if other is provided
one field is required if other is provided

Time:09-20

I am trying to define a type

type myType{
 ....
 counter?: number,
 maxCount?: number,
}

with counter being required if (and only if) maxCount is provided. What are the options to specify it in typescript?

CodePudding user response:

You will need MyType to be a union type where one member of the union requires the relevant properties, while the other member either prohibits them or makes them optional, depending on the use case.

TypeScript doesn't have an "official" or direct syntax for prohibiting a property. Merely leaving the property out of the type definition doesn't really have this effect, especially with unions. See Why does A | B allow a combination of both, and how can I prevent it? for more information.

Instead, you can make the property optional with a value of the impossible never type. Since you cannot find a value of the never type, an optional property of type never can only meaningfully be satisfied by not including the property at all.

I'd recommend making a base type that your union members extend.

interface BaseMyType {
  someProp: string;
  counter?: number;
  maxCount?: number;
}

If you want to allow either both maxCount and counter or neither of them to appear, then you can do it like this:

interface MyTypeWithCounter extends BaseMyType {
  counter: number,
  maxCount: number
}

interface MyTypeWithoutCounter extends BaseMyType {
  counter?: never,
  maxCount?: never;
}

type MyType = MyTypeWithCounter | MyTypeWithoutCounter

And test it:

let myType: MyType;
myType = { someProp: "abc", counter: 123, maxCount: 456 }; // okay
myType = { someProp: "abc", maxCount: 456 }; // error
myType = { someProp: "abc", counter: 123, }; // error
myType = { someProp: "abc", }; // okay

On the other hand, if you just want to make sure that counter appears if maxCount does, and otherwise counter should be allowed but not required, then you can write it this way:

interface MyTypeWithMaxCount extends BaseMyType {
  counter: number,
  maxCount: number
}

interface MyTypeWithoutMaxCount extends BaseMyType {
  maxCount?: never;
}

type MyType = MyTypeWithMaxCount | MyTypeWithoutMaxCount

And test it:

let myType: MyType;
myType = { someProp: "abc", counter: 123, maxCount: 456 }; // okay
myType = { someProp: "abc", maxCount: 456 }; // error
myType = { someProp: "abc", counter: 123, }; // okay
myType = { someProp: "abc", }; // okay

Playground link to code

  • Related