Home > Software engineering >  Typescript: enforce prop if another prop present in object
Typescript: enforce prop if another prop present in object

Time:05-19

How can I enforce the presence of a property when another property in the same object is present?

I am trying to enforce that if a Period has a start then end needs to be present, and vice-versa.

However, in my example period ends up as type Period even tho it has an end property, I'd expected typescript to say Property 'start' is missing in type '{ day: 1; end: string; }' but required in type 'FullPeriod'

type DefaultPeriod = {
  day: 1 | 2 | 3 | 4 | 5 | 6 | 7
}

type FullPeriod = DefaultPeriod & {
  start: string
  end: string
}

type Period = DefaultPeriod | FullPeriod

const period: Period = {
  day: 1,
  end: '00:00',
}

type Test = typeof period extends FullPeriod ? true : false // False as expected
type TestB = typeof period extends DefaultPeriod ? true : false // True ???

CodePudding user response:

The reason that the code is not failing for the period constant is because TypeScript will allow extra properties to be specified if they are in other branches of a union. Therefore, it thinks period is a DefaultPeriod but it will allow start and end. To fix this, you must make the unions discriminated by those properties so they must be all or nothing. You can easily do this by intersecting your DefaultPeriod type with an object type of the keys of FullPeriod set to never and optional so they must not be specified when you are in the DefaultPeriod branch.

type DefaultPeriod = {
  day: 1 | 2 | 3 | 4 | 5 | 6 | 7;
}

type FullPeriod = DefaultPeriod & {
  start: string
  end: string
}

type Period = (DefaultPeriod & Partial<Record<Exclude<keyof FullPeriod, "day">, never>>) | FullPeriod

// @ts-expect-error ✅ Correctly Fails
const test1: Period = {
  day: 1,
  end: '00:00',
}

// @ts-expect-error ✅ Correctly Fails
const test2: Period = {
  day: 1,
  start: '00:00',
}

// ✅ Correctly Passes
const test3: Period = {
  day: 1,
}

TypeScript Playground Link

  • Related