简体   繁体   中英

Best practices for managing optional interface properties in Typescript?

I have an interface with the following properties:

interface ISchedule {
  date: string;
  startTime: string;
  endTime?: string;
}

now the endTime property here is optional because it is undefined when an object is created using the interface. It is defined later on in the codebase. Once it is defined, i have to pass the object to a function where the type of the object is ISchedule.

Now inside the function, if i try to use the endTime property, i get:

"Object is possibly undefined"

Now there are two ways to handle this:

  1. add an if check
  2. use ! to bypass null/undefined checks

In my opinion, adding an if check around the property is redundant/dangerous since i am sure that the property will always exist/has to exist in the respective function. The function's behavior will change if the property does not exist.

However, a few team mates disagree with this and insist on using the if check.

I wanted to know what are the best practices for handing optional properties in Typescript and what is the ideal way to handle this specific situation as this is occurring in alot of places in the codebase.

Looking forward to the solutions!

I agree with your colleagues that you should put the if check in place. The reason for this is that your code will be much more robust. Maybe the endTime is currently always set, but later on, if the implementation changes on either end, you will still have to keep in mind the decision you have taken and if it's somebody else working on the code, it can easily introduce bugs.

The whole point of undefined values is, what to do when you do not have them? You could simply set a default value using destructuring

const {endTime = "00:00"} = getSchedule();

In this way, endTime will always be undefined

Or… you could also set defaults values to the entire object

const neverNull = {...getSchedule(), {endTime:"00:00"}}

Now personally I try to never write to plain objects directly. In your case I would very likely wrap those data into an Adapter as follow:

export abstract class AbstractAdapter<T> {
  constructor(protected data: T) {

  }

  public getData(): T {
    return this.data;
  }
}

Then:

class ScheduleAdapter extends AbstractAdapter<ISchedule>{
public getEndTime(): string {
  return this.getData().endTime || this.defaultEndTime();
}

private defaultEndTime():string {
  return "00:00";
}
}

usage

const schedule = new ScheduleAdapter(getSchedule());
console.log(schedule.getEndTime());

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM