This question is about a TypeScript code-base, but the general concepts that underpin it are ECMAScript concepts, therefore; I have tagged the question as both JS & TS. If I should tag it differently, you can either let me know, or make the edit yourself and I will accept it if I see it.
In a TypeScript document, I have defined an object-type that contains 4 string
typed properties.
type StrObjType = {
foo?: string;
boo?: string;
fuz?: string;
buz?: string;
}
Along with the object described above, I have a function — its called funkyFunc
— with a parameter that takes an argument of type StrObjType
.
function funkyFunc(obj:StrObjType)
{
let mesg = '';
if(obj.foo){ mesg += '\n Foo: ' + obj.foo; }
if(obj.boo){ mesg += '\n Boo: ' + obj.boo; }
if(obj.fuz){ mesg += '\n Fuz: ' + obj.fuz; }
if(obj.buz){ mesg += '\n Buz: ' + obj.buz; }
if(!mesg) return '';
// If a string was concatenated to mesg, return w/ newline appended
return mesg + '\n';
}
If you can't tell by looking at it, the function creates a list using only properties that were added to the functions object-argument. The new-lines ('\n'
) are added as a means to format the list.
You can see in the function that each property in the object's type is checked for a truthy value. When a property is truthy, the props string-value is appended to the mesg
variable via the "Addition Assignment Operator".
What I'd like to know, is if there is a better way to write the if statements, if I can short circuit them, or use a shorthand. Here is an example of a short circuit that uses nullish coalescing. The link shows a short circuit used to check an objects property, and assign it, only if the property is non-nullish (null or undefined). I was hoping to use that operator, but becauese I am appending the value when the property exist, rather than assigning it, the nullish coalescing won't seem to work. Nevertheless, I can't help but to feel like this is the ideal situation for implementing a short circuit, but if there is a better way to write the function, I can't figure out what it is.
let mesg = '';
mesg += obj.foo ? '\n Foo: ' + obj.foo : undefined;
mesg += obj.boo ? '\n Boo: ' + obj.boo : undefined;
mesg += obj.fuz ? '\n Fuz: ' + obj.fuz : undefined;
mesg += obj.buz ? '\n Buz: ' + obj.buz : undefined;
if (!mesg) return '';
return mesg + '\n';
}
The problem with the above method of writing the function is that it requires using the JS built-in undefined
constant, and that feels hacky to me, as the undefined
constant wouldn't be needed otherwise. I could replace the undefined
constant with an empty string I guess, but it still doesn't feel right, as it requires adding a nullish value, which are values I typically try to avoid (not that I see any immediate harm that comes from using undefined
, or ''
).
Is there a shorthand, or short-circuit I can use to eliminate the if keywords in the first function example I added? The short circuit or shorthand cannot be the ternary example I already provided, and it needs to shorten the codes length, or improve the codes readability.
If there is any confusion, or if the question just isn't making sense, let me know, I will clear things up. I am peer reviewed, and I feel like there is something I am missing here. I would like to feel more confident about how I am authoring this function.
There is this way of writing it, but IDK if it's better.
What you have after the text above will result in the string "undfined"
appearing in mesg
, which I assume you don't want. :-) If you wanted to do it that way, you'd use ""
, not undefined
, for the third operand of the conditional operators:
mesg += obj.foo ? "\n Foo: " + obj.foo : "";
mesg += obj.boo ? "\n Boo: " + obj.boo : "";
mesg += obj.fuz ? "\n Fuz: " + obj.fuz : "";
mesg += obj.buz ? "\n Buz: " + obj.buz : "";
What I'd like to know, is if there is a better way to write the if statements, if I can short circuit them, or use a shorthand.
Not really. Moreover, what you have with the if
statements (or the series of conditional operator expressions) is clear and easy to debug. There are other ways to do it, but they may be harder for people coming to the code fresh to understand.
For example, you could have this constant:
const messageParts = [
{key: "foo", label: "Foo"},
{key: "boo", label: "Boo"},
{key: "fuz", label: "Fuz"},
{key: "buz", label: "Buz"},
] as const;
(The as const
is important, that way TypeScript can see that the key
values are valid keys for StrObjType
.)
Then the code is something like:
for (const {key, label} of messageParts) {
const value = obj[key];
if (value) {
mesg += `\n ${label}: ${value}`;
}
}
For a one-off, that's definitely not an improvement, and it's certainly not short-circuiting or shorthand. But if there are a lot of places you use this same information, it gives you just one place to maintain it.
But if it's a one-off just to build mesg
, I think you're best off with the if
s you already have, or the conditional operator expression alternative you noted (with the undefined
=> ""
fix).