How do i exclude namespace functions when iterating over an enum? - javascript

I have the following code
export enum JournalEntryType {
ORDINARY_JOURNAL_ENTRY = 'ORDINARY_JOURNAL_ENTRY',
PLANT_TRANSPLANTED = 'PLANT_TRANSPLANTED',
SOW_SEEDS = 'SOW_SEEDS',
SEEDS_GERMINATED = 'SEEDS_GERMINATED',
PLANT_BLOSSOMS = 'PLANT_BLOSSOMS',
FRUIT_SETTING = 'FRUIT_SETTING',
FRUIT_CHANGED_COLOR = 'FRUIT_CHANGED_COLOR',
HARVEST = 'HARVEST',
ANIMAL_SIGHTING = 'ANIMAL_SIGHTING'
}
export namespace JournalEntryType{
export function getJournalEntryTypeColor(journalEntryType: string): string{
switch(journalEntryType){
case JournalEntryType.ORDINARY_JOURNAL_ENTRY.toString(): return '#FFFFFF';
case JournalEntryType.PLANT_TRANSPLANTED.toString(): return '#8B4513';
case JournalEntryType.SOW_SEEDS.toString(): return '#D2691E';
case JournalEntryType.SEEDS_GERMINATED.toString(): return '#7CFC00';
case JournalEntryType.PLANT_BLOSSOMS.toString(): return '#FFB6C1';
case JournalEntryType.FRUIT_SETTING.toString(): return '#FF69B4';
case JournalEntryType.FRUIT_CHANGED_COLOR.toString(): return '#ff1493';
case JournalEntryType.HARVEST.toString(): return '#DC143C';
case JournalEntryType.ANIMAL_SIGHTING.toString(): return '#800080';
default: throw new Error();
}
}
}
When i iterate over JournalEntryType and log every value like so:
for(let journalType in JournalEntryType){
console.log(journalType);
}
The last value that is printed won't be ANIMAL_SIGHTING but getJournalEntryTypeColor. In other words, it also iterates over any functions that are declared in the namespace. How do i prevent this from happening? I've tried filtering out the method with an if statement that checks whether the type of the journalTypeis a string. But that won't stop from getJournalEntryTypeColor getting printed as well.

The key thing is that you need to check the type of the property on the object, not the type of the key.
Object.keys(JournalEntryType)
.filter(journalType => !(typeof(JournalEntryType[journalType]) === 'function'))
.forEach(journalType => {
//code here
});
(This code is taken from the comments, it was written by Maurice.)

Related

document.createElement variable return from a function as undefined. Why?

I have this code:
/* Creating whole HTML Units from HTML Element list */
function createHTMLUnit(unitstruct){
var tempElement;
Object.entries(unitstruct).forEach(([key, value]) => {
// First of all we start a loop
if(typeof value==='object'&&key==='$child'){ //Find out that we have an Object and it's a child
//If yes we append this as a child to the tempElement with call this function again
tempElement.appendChild(createHTMLUnit(value));
}else{
//If not we reach a single element have to find out what we should do with it
//Switch from the cases like '$tag', '$attr', '$child' or other (will be a value)
switch(key){
case '$tag': //Createing the element in value
tempElement = document.createElement(value);
break;
case '$attr': //Loop through the value and set attributes for the element
Object.entries(value).forEach(([attrkey, attrvalue]) => {
tempElement.setAttribute(attrkey, attrvalue);
})
break;
case '$child': //Element innerHTML
return tempElement;
break;
default: //Return with the value to the previous loop
return tempElement;
break;
}
}
});
var htmlUnitParam = {
'$tag': 'div',
'$attr': {
'class':'msg-box',
'id':'msg-box01'
},
'$child': {
'$tag': 'div',
'$attr': {
'class':'msg-box-container',
'id':'msg-box-container01'
},
'$child': ''
}
}
document.body.appendChild(createHTMLUnit(htmlUnitParam));
If I run this it will be drop an "Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'." error. If I take things apart like:
var x = createHTMLUnit(value);
tempElement.appendChild(x);
'x' gonna be undefined after createHTMLUnit returns (as I checked in Chrome Inspector, at the point'return tempElement;' tempElement hold it's value and the function return undefined...) so appendChild can't apply anything to the node.
Anybody have any idea what is the problem? And how can I fix it?
(And yes, I should use recrusion and a function like this one...)
You are trying to return a value from the function, but your return statements are inside a forEach loop callback function. Returning inside the loop doesn't return from the outer function, so at the end of the loop it just returns nothing.
Related info on forEach return
That link contains some other alternatives, but basically you want to replace the forEach loop with a regular for loop, going through the Object.keys or Object.values and then your returns should start to function as you originally expected.
Thanks for everybody!
Meanwhile I just find an other way (maybe it's a bit comfortable as well) to get it to work.
function createHTMLUnit(unitstruct, appendto){
var tempElement;
Object.entries(unitstruct).forEach(([key, value]) => {
if(typeof value==='object'&&key==='$child'){
createHTMLUnit(value, tempElement);
}else{
switch(key){
case '$tag':
tempElement = document.createElement(value);
break;
case '$attr':
Object.entries(value).forEach(([attrkey, attrvalue]) => {
tempElement.setAttribute(attrkey, attrvalue);
})
break;
case '$child':
tempElement.innerHTML = value;
return;
break;
default:
createHTMLUnit(value, appendto);
break;
}
}
});
if(tempElement!=undefined){
appendto.appendChild(tempElement);
}
}
Maybe it's not that nice but it works fine.

How to map an response when It's array and when It's not?

I just coded two simple functions to validate a field if it has a specific value, but in some cases the responses could be a array. Can I simplify these function in just one function? I tried to use the same "isOrderInLastMile" function when the response is just a object but I retrieve the "object.map is not a function" error.
function 1: I use this function when the response is an array of objects.
export function isOrderInLastMile(pieces: any): boolean {
const totalPieces: number = pieces.length;
let momentFound: number = 0;
pieces.map((element) => {
if (
element.stagesHistory &&
getCurrentMoment(element.stagesHistory) ===
MomentsLabel.En_Ășltima_milla_id
) {
momentFound = momentFound + 1;
}
return momentFound;
});
return totalPieces === momentFound;
}
Function 2: I use this function when this response is just an object
export function isPieceInLastMile(piece: any): boolean {
return (
piece.stagesHistory &&
getCurrentMoment(piece.stagesHistory) === MomentsLabel.En_Ășltima_milla_id
);
}
How Can I unify both functions? Thanks!!! :)
Either wrap your single element in an array, like this:
export function isPieceInLastMile(piece: any): boolean {
return isOrderInLastMile([piece]);
}
or use the single function in a callback
return pieces.every(isPieceInLastMile);
(The latter option can be used instead of counting all matching items)

How to fix the issue not all the code paths return value?

I have an error in the code that I am trying to resolve. I think it needs a return statement but I have that already outside of the forEach loop but it still throws the error:
not all the code path return the value
How to fix below code?
main.ts:
private ValidateRequestArgs(str) {
let ret: boolean = true;
// here on val its throwing tslint error not all code paths return value
str.split(',').forEach((val) => {
if (!ret) {
return false;
}
if (List.indexOf(val) > -1) {
ret = true;
} else {
ret = false;
}
});
return ret;
}
The complaint is that the first if(){} is missing an else{} block with a return statement. You can disable this behaviour in a tsconfig file setting:
"noImplicitReturns": false,
Of course you could also add
else {return ...}
But I would not recommend that, since forEach is not supposed to return anything as stated for example here:
What does `return` keyword mean inside `forEach` function?
or here:
https://codeburst.io/javascript-map-vs-foreach-f38111822c0f
Instead better get rid of the first if() altogether. Cheers
tsconfig.json
compilerOptions:{
"noImplicitReturns": false
}
You can resolve this error by two ways.
By editing the noImplicitReturns attribute to false in tsconfig.json
"noImplicitReturns": false
By adding a return statement for every path in your method. If you have 10 if conditions, then you have to add 10 return statements. It seems odd, but typescript recommends return for every path.
Here we can avoid the number of paths by the use of lambda expression.
private ValidateRequestArgs(str) {
return str.split(",").every(el => List.includes(el));
}
I'm not sure why tslint is complaining, but you can write the whole thing way more elegant as:
return str.split(",").every(el => List.includes(el));
or ES6:
return str.split(",").every(el => List.indexOf(el) > -1);
If there is a return in the forEach function, then it recognizes the function as having a return type for the whole function.
There is no problem to write your loop this way:
myFunction(): boolean {
for (let item of items) {
if (condition) {
return true;
}
}
return false;
}
foreach no need to return anything; you must be remove keyword 'return' from foreach and edit and the error points to this:
private ValidateRequestArgs(str) {
let ret: boolean = true;
// here on val its throwing tslint error not all code paths return value
str.split(',').forEach((val) => {
if (!ret) {
ret = false // this correct
}
if (List.indexOf(val) > -1) {
ret = true;
} else {
ret = false;
}
});
return ret;
}
You can define the returned value for you function like the following:
functionName: (variableName: string): string | void => {
if (variableName === "a") {
return "link";
}
};
The body of the function passed to forEach has an implicit signature of any -> boolean so it would seem that tslint is expecting you to treat it more statically and return boolean on all code paths.
if you are facing this using .forEach for-loop use for-of for-loop as below,
for (var val of str.split(',')) { }

JavaScript alternatives to handling switch cases that have properties

I have HTML elements defined like this
<div id="Jacob" validation="required alpha-numeric"></div>
<div id="Peter" validation="required minlen:4 maxlen:20"></div>
And in Javascript I was parsing and handling the validation="property1 property2 ... propertyN" like this:
// called from a foreach that uses split(' ')
validate(type) {
switch(type) {
case "required":
// ...
break;
case "alpha-numeric":
// ...
break;
}
}
I realise using a switch like this might be a bit verbose and archaic.
What would be an elegant way to parse parameters that have their own properties/values?
I don't know where you got the impression that switch statements are archaic. They're not.
As far as your particular case, you can use .split(":") to split the individual parts apart, and then match on that:
function validate(type) {
var parts = (type || "").split(":");
switch(parts[0].toLowerCase()) {
case "required":
// ...
break;
case "alpha-numeric":
// ...
break;
case "minlen":
// validate against parts[1]
break;
}
}
If you wanted to use a lookup rather than a switch, you can do that, but I'd say that's just a matter of preference:
var validators = {
"required": function (value) {
},
"alpha-numeric": function (value) {
},
"minlen": function (value, len) {
}
};
function validate (value, type) {
var parts = (type || "").split(":");
var validator = validators[parts[0].toLowerCase()];
if (validator) {
var result = validator(value, parts[1]);
}
}
One potential benefit of the second approach is that new validators can be added to the validator object at runtime, though if you wanted to make a pluggable system, you'd probably want to go with something more robust than just a plain object that can be arbitrarily modified.
you can create json of key function pair. And pick function using your type and call it.
var objOfFunction = {};
objOfFunction["required"] = requiredFunction;
objOfFunction["alpha-numeric"] = alphanFunction;
function validate(type) {
objOfFunction[type]();
}
function requiredFunction(){
alert("required");
}
function alphanFunction(){
alert("in alpha");
}
validate("required");
validate("alpha-numeric");

Can you access local static variables within local method in TypeScript?

Lets say I have a class like below:
class Types
{
static TypeOne = 1;
static TypeTwo = 2;
static TypeThree = 3;
static TypeFour = 4;
static TypeFive = 5;
public GetNameFromType(type: number) : string
{
switch (type)
{
case Types.TypeOne: return "TypeOne";
case Types.TypeTwo: return "TypeTwo";
case Types.TypeThree: return "TypeThree";
case Types.TypeFour: return "TypeFour";
case Types.TypeFive: return "TypeFive";
default: return "Unknown";
}
}
}
Now after reading some documentation on static classes it seems like the above should work. however I keep getting an error saying that Types.TypeOne does not exist in the current scope.
So do I need to do anything else or should this just work?
== Edit ==
I didnt think it was outputting anything for it due to the errors, but it appears it has, here is the output:
var Types = (function () {
function Types() { }
Types.TypeOne = 1;
Types.TypeTwo = 2;
Types.TypeThree = 3;
Types.TypeFour = 4;
Types.TypeFive = 5;
Types.TypeSix = 6;
Types.prototype.GetNameFromType = function (type) {
switch(type) {
case AbilityTypes.TypeOne:
return "TypeOne";
case AbilityTypes.TypeTwo:
return "TypeTwo";
case AbilityTypes.TypeThree:
return "TypeThree";
case AbilityTypes.TypeFour:
return "TypeFour";
case AbilityTypes.TypeFive:
return "TypeFive";
case AbilityTypes.TypeSix:
return "TypeSix";
default:
return "Unknown";
}
};
return Types;
})();
This looks legit, so maybe it is working and is just complaining...
Are you using Typescript 0.8.3? Your code compiles fine for me. If the error is happening else where, try and define them as a 'public static'
Pasting that code into the Playground ( http://www.typescriptlang.org/playground ) shows no errors or warnings for me, and this is running the same 0.8.3 bits as the MSI. I would uninstall/reinstall if you are seeing errors with just this code, as unless this is part of a larger project and the error is occurring elsewhere, then seems like something in the install must be out of whack.

Categories

Resources