0

given the following types

enum Modes {
  error,
  warning
}

type Notification = {
  title: string;
  text: string;
};

type CustomNotification = Notification & { mode: Modes };

interface Options {
  defaultNotification: Notification;
  customNotification?: CustomNotification;
}

I want to assign a variable to customNotification if available, otherwise to defaultNotification, hence I use the logical OR operator in assignment, as:

const notification = customNotification || notificationDefault;

then I want to conditionally execute logic depending on the value of mode if available.

if (notification.mode && notification.mode !== Modes.error) { /** code here */ }

However, notification is only assigned to type Notification rather than CustomNotification | Notification, thus typescript throws an error when trying to read notification.mode value Property 'mode' does not exist on type 'Notification'.

I have even tried to explicitly assign notification type to CustomNotification | Notification but that did not work.

I don't see why this isn't working, and I wonder if there's a workaround other than refactoring my code to use two variables instead?

1 Answer 1

3

The main issue is that mode is a field which does not exists in one member of Notification | CustomNotification union. Checking mode field for object being Notification is operation not allowed, as it has no such field. Below my propositions how to deal with your problem.

Solution one - type unification with default mode

I would consider instead of having dual types here, have one type, and introduce inside Modes some neutral element lets say - default, when we do so all types problem go away, and we don't need to do any type assertions or guards. Consider:

enum Modes {
  error,
  warning,
  default, // neutral value
}

type Notification = {
  title: string;
  text: string;
  mode: Modes;
};

interface Options {
  defaultNotification: Notification;
  customNotification?: Notification;
}

// getting active notification helper
const getNotification = (options: Options): Notification => {
    return options.customNotification ?? options.defaultNotification;
}

// using
const notification = getNotification(options);
if (notification.mode !== Modes.error) { /** code here */ }

The only thing we need to do is set defaultNotification to an object with mode equal Modes.defalut.

Solution two - mode as explicit undefined field

Eventually if you want to keep Modes in current shape, we can introduce mode field as an undefined field in defaultNotification. Consider following:

type BaseNotification = {
  title: string;
  text: string;
};

type DefNotification = BaseNotification & { mode?: undefined } // pay attention here
type CustomNotification = BaseNotification & { mode: Modes }

type Notification = DefNotification | CustomNotification;

interface Options {
  defaultNotification: DefNotification;
  customNotification?: CustomNotification;
}

const getNotification = (options: Options): Notification => {
    return options.customNotification ?? options.defaultNotification;
}

The main point here is { mode?: undefined }, we say that our DefNotification has mode field but the only possible value for it is undefined.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.