1

I have a Typescript project in which I need to build a dynamic if from the data I receive from the object, I have to add as many conditions as properties the object has.

This is the object:

interface EmailParams { 
  sender?: string,
  subject?: string,
  fileType?: string
}

let params: EmailParams = {
  sender: "test@gmail.com"
}

This is what I need:

if (test1.from.toUpperCase() == params.sender?.toUpperCase())

But if the object changes, this is a new object:

interface EmailParams { 
  sender?: string,
  subject?: string,
  fileType?: string
}

let params: EmailParams = {
  sender: "test@gmail.com",
  subject: "data"
}

This should be the new if:

if (test1.from.toUpperCase() == params.sender?.toUpperCase() &&
    test1.data.toUpperCase() == params.subject?.toUpperCase()
)
segmet
  • 61
  • 1
  • 7
  • you can try nested if conditions like `if(params.sender) { if(params.subject) { if(params.fileType){ "if with 3 &&&" } else { "if with 2 &&" } } else { "if with 1 &" } } else { console.log("nothing satified") }` – Usama Jun 07 '22 at 10:14
  • 1
    @Usama that might *work* but it's not great advice, complex nested conditionals are hard to read, hard to follow, hard to maintain, hard to change... – Jared Smith Jun 07 '22 at 10:34
  • 1. Extract this nested condition to a function returning a boolean. This way, you have an option of return at any point of the function, instead of complex nesting. 2. Use `'key' in obj` or `obj.hasOwnProperty('key') to check if your criteria object has a property. – Lesiak Jun 07 '22 at 10:52
  • @Lesiak although I'm not a purist I would argue that just like deeply nested conditionals multiple exit points for a function are also [something of a code smell](https://stackoverflow.com/a/733858/3757232). I'm not saying that I *never* do it but I would hesitate before I would give that as *unqualified advice* to someone asking for such. – Jared Smith Jun 07 '22 at 12:28

1 Answers1

1

You can do this generally by doing something like this:

const keys: (keyof EmailParams)[] = [
  'sender',
  'subject',
];

const test = {
  data: "whatever",
  from: "foobar",
};

const testToEmailParamsMapping: {
  [k in keyof EmailParams]: keyof typeof test
} = {
  subject: 'data',
  sender: 'from',
}

const testEmailParams = (obj: EmailParams): boolean => {
  return keys.every((k: keyof EmailParams) => {
    if (k in testToEmailParamsMapping) {
      // cast is safe because of the check, not sure why compiler doesn't follow
      const testKey: keyof typeof test = testToEmailParamsMapping[k] as keyof typeof test;
      return obj[k]?.toUpperCase() === test[testKey].toUpperCase();
    }
    return true; // we don't care about the keys we don't care about
  });
};

console.log(testEmailParams(params));

If you add a property then there are only two places you need to update: the list of keys, and the mapping that correlates keys in the email params with keys in the test object.

This has some other advantages: you can easily write unit tests for this in a way that would be error-prone with your conditional logic (did you cover every possible branching?) and it's now abstracted into a well-named function you can pass to other parts of the code.

Playground

Jared Smith
  • 19,721
  • 5
  • 45
  • 83
  • I understand how this function works, what I don't understand is how to obtain the dynamic conditions that I want to add from this function. In addition, the variables test1.from and test1.data are fixed as long as the property is informed – segmet Jun 07 '22 at 11:01
  • @segmet the keys.every will test each property in the test object against the email params just like your conditions do but without having to write them out by hand, and as I said in the answer as long as you update the list of keys and the mapping between the names of the keys in the test object and the names of the keys in the interface it will work. You could make it entirely dynamic (if such is desired) by making the list of keys and the mapping parameters to the function. – Jared Smith Jun 07 '22 at 11:05