Fwd: withBreak blocks

# sagiv ben giat (6 years ago)

I hope I'm on the right medium for this, I would like to propose a language feature. withBreak blocks

Well the name might not be the best, but it's just my way to be the most clear about this feature proposal.

I often find my self doing this in my code:

const doWork = () => {
  // try catch omitted for brevity
  const response = fetchData();
  do {
    if (response.error) {
      log(response.message);
      break;
    }
    if (!response.data) {
      log("No data");
      break;
    }
    if (!response.data.todos) {
      log("No Todos");
      break;
    }
    return action({ data: response.data });
  } while (false);
};

I'm doing this instead of doing bunch of if / else if / else blocks or ugly nested if blocks. What i would like to have is a block that will let me break without being in a loop context. Something like this:

withBreak {
  if (response.error) {
    log(response.message);
    break;
  }
  if (!response.data) {
    log("No data");
    break;
  }
  if (!response.data.todos) {
    log("No Todos");
    break;
  }
  return action({ data: response.data });
}

This can be a synthetic sugar for do{}while(false) behind the scenes.

Best ,

Sagiv B.G

# Jacob Pratt (6 years ago)

Perhaps I'm missing something, but why wouldn't an else if work here?

jhpratt

# sagiv ben giat (6 years ago)

else if would work but it will look kinda nasty if you have deeply nested validations. Keep in mind my example was a small and simple. It's the same as using async await vs .then etc...

Sagiv b.g

# Oriol _ (6 years ago)

This is so close to your proposal, and already works right now:

block: {
  if (response.error) {
    log(response.message);
    break block;
  }
  if (!response.data) {
    log("No data");
    break block;
  }
  if (!response.data.todos) {
    log("No Todos");
    break block;
  }
  return action({ data: response.data });
}
# Peter Jaszkowiak (6 years ago)

Also, you can just use return if you're in a function:

const doWork = () => {
  // try catch omitted for brevity
  const response = fetchData();

  if (response.error) {
    log(response.message);
    return;
  }
  if (!response.data) {
    log("No data");
    return;
  }
  if (!response.data.todos) {
    log("No Todos");
    return;
  }

  return action({ data: response.data });
};
# sagiv ben giat (6 years ago)

This is so close to your proposal, and already works right now: Yeah, i just thought labels will become deprecated at some point and never thought of using them. good point though :)

Sagiv b.g

# sagiv ben giat (6 years ago)

Also, you can just use return if you're in a function:

That's not what i'm looking for. I may want to do some more things inside the function besides validations.

Sagiv b.g

# Jacob Pratt (6 years ago)

Can you provide a clear use case that can't (or shouldn't) be covered by what others have mentioned?

jhpratt

# sagiv ben giat (6 years ago)

Can you provide a clear use case that can't (or shouldn't) be covered by what others have mentioned?

The label: suggestion kinda nails it, though i was sure i read somewhere that labels should not be used anymore.

Sagiv B.G

# sagiv ben giat (6 years ago)

Can you provide a clear use case that can't (or shouldn't) be covered by what others have mentioned?

Actually es-lint won't allow it: eslint.org/docs/rules/no-labels

Sagiv B.G

# Peter Jaszkowiak (6 years ago)

What kind of argument is that? ESlint isn't a JavaScript runtime, it is fully configurable, and I don't see how it's at all relevant.

# sagiv ben giat (6 years ago)

What kind of argument is that? ESlint isn't a JavaScript runtime, it is fully configurable, and I don't see how it's at all relevant.

I know ESLint can be configured, it was just an example for how label statements are considered as poor design of code.

Sagiv B.G

# 李白|字一日 (6 years ago)

you can simply put these value handler pairs into an array.

const a = [[0, function(){}], [1, function(){}]]; let i = 0;

while (a[i++][0]) { a[i - 1]1 break; }

2018-02-18 5:58 GMT+08:00 sagiv ben giat <sagiv.bengiat at gmail.com>:

# Jordan Harband (6 years ago)

Your proposal is conceptually the same as a labelled break statement (ie, GOTO); if you want to follow the advice to avoid labels, I suspect it would apply to your proposal as well.

# kai zhu (6 years ago)

On Feb 18, 2018, at 4:52 AM, sagiv ben giat <sagiv.bengiat at gmail.com> wrote:

Can you provide a clear use case that can't (or shouldn't) be covered by what others have mentioned?

@sagiv, if you need guidance by use-case/example, here’s a real-world example [1] of a validator “god” function (with 100% code-coverage [2]) that encapsulates most of the logic for validating user-inputs against the full swagger/openapi 2.0 spec [3]. attached screenshot showing how its used from browser-console (works just as well in nodejs).

and yes, the code-sample makes use of break statements in:

  1. a recursive while loop to dereference schema pointers [4]
  2. in switch/case blocks which are conceptually similar to what you want to do

[1] kaizhu256/node-swgg/blob/2018.2.1/lib.swgg.js#L4076, kaizhu256/node-swgg/blob/2018.2.1/lib.swgg.js#L4076 [2] kaizhu256.github.io/node-swgg/build..beta..travis-ci.org/coverage.html/node-swgg/lib.swgg.js.html, kaizhu256.github.io/node-swgg/build..beta..travis-ci.org/coverage.html/node-swgg/lib.swgg.js.html [3] OAI/OpenAPI-Specification/blob/3.0.1/versions/2.0.md, OAI/OpenAPI-Specification/blob/3.0.1/versions/2.0.md [4] OAI/OpenAPI-Specification/blob/3.0.1/versions/2.0.md#referenceObject, OAI/OpenAPI-Specification/blob/3.0.1/versions/2.0.md#referenceObject

/*
 * real-world example of swagger-validator from
 * https://github.com/kaizhu256/node-swgg/blob/2018.2.1/lib.swgg.js#L4076
 */

/*jslint
    bitwise: true,
    browser: true,
    maxerr: 8,
    maxlen: 100,
    node: true,
    nomen: true,
    regexp: true,
    stupid: true
*/

local.validateBySwaggerSchema = function (options) {
/*
 * this function will validate options.data against the swagger options.schema
 * according to the spec defined at:
 * http://json-schema.org/draft-04/json-schema-validation.html#rfc.section.5
 */
    var $ref,
        circularList,
        data,
        dataReadonlyRemove2,
        ii,
        oneOf,
        schema,
        test,
        tmp;
    if (!options.schema) {
        return;
    }
    data = options.data;
    options.dataReadonlyRemove = options.dataReadonlyRemove || [{}, '', null];
    dataReadonlyRemove2 = options.dataReadonlyRemove[2] || {};
    schema = options.schema;
    circularList = [];
    while (true) {
        // dereference schema.schema
        while (schema.schema) {
            schema = schema.schema;
        }
        // dereference schema.oneOf
        oneOf = (data && schema.oneOf) || [];
        for (ii = 0; ii < oneOf.length; ii += 1) {
            tmp = String(oneOf[ii] && oneOf[ii].$ref)
                .replace('http://json-schema.org/draft-04/schema#', '#');
            switch (tmp + ' ' + (!local.isNullOrUndefined(data.$ref) || data.in)) {
            case '#/definitions/bodyParameter body':
            case '#/definitions/formDataParameterSubSchema formData':
            case '#/definitions/headerParameterSubSchema header':
            case '#/definitions/jsonReference true':
            case '#/definitions/pathParameterSubSchema path':
            case '#/definitions/queryParameterSubSchema query':
                schema = local.swaggerSchemaJson.definitions[tmp.split('/')[2]];
                break;
            default:
                switch (tmp) {
                case '#/definitions/bodyParameter':
                case '#/definitions/jsonReference':
                    schema = oneOf[ii ^ 1];
                    break;
                }
            }
            if (!schema.oneOf) {
                break;
            }
        }
        // dereference schema.$ref
        $ref = schema && schema.$ref;
        if (!$ref) {
            break;
        }
        test = circularList.indexOf($ref) < 0;
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'schemaDeferenceCircular',
            prefix: options.prefix,
            schema: schema
        });
        circularList.push($ref);
        tmp = $ref.split('/').slice(-2);
        schema = $ref.indexOf('http://json-schema.org/draft-04/schema#/') === 0
            ? local.swaggerSchemaJson[tmp[0]]
            : options.swaggerJson[tmp[0]];
        schema = schema && schema[tmp[1]];
        test = schema;
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'schemaDeference',
            prefix: options.prefix,
            schema: options.schema
        });
    }
    if (options.modeDereference) {
        if (options.modeDereferenceDepth > 1) {
            schema = local.jsonCopy(schema);
            Object.keys(schema.properties || {}).forEach(function (key) {
                schema.properties[key] = local.validateBySwaggerSchema({
                    // dereference property
                    modeDereference: true,
                    modeDereferenceDepth: options.modeDereferenceDepth - 1,
                    prefix: options.prefix.concat(['properties', key]),
                    schema: schema.properties[key],
                    swaggerJson: options.swaggerJson
                });
            });
        }
        return schema;
    }
    // validate schema.default
    if (options.modeDefault) {
        data = schema.default;
    }
    // validate semanticRequired
    test = options.modeDefault ||
        !local.isNullOrUndefined(data) ||
        schema.required !== true ||
        schema['x-swgg-notRequired'];
    local.throwSwaggerError(!test && {
        data: data,
        errorType: 'semanticRequired',
        prefix: options.prefix,
        schema: schema
    });
    if (local.isNullOrUndefined(data)) {
        return;
    }
    // validate semanticRequiredArrayItems
    test = !options.modeSchema || local.schemaPType(data) !== 'array' ||
        (typeof local.schemaPItems(data) === 'object' && local.schemaPItems(data));
    local.throwSwaggerError(!test && {
        errorType: 'semanticRequiredArrayItems',
        prefix: options.prefix,
        schema: data
    });
    // remove readOnly property
    if (schema.readOnly) {
        delete options.dataReadonlyRemove[0][options.dataReadonlyRemove[1]];
    }
    // optimization - validate schema.type first
    // 5.5.2. type
    // https://swagger.io/docs/specification/data-models/data-types/
    // https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md#data-types
    switch (local.schemaPType(schema)) {
    case 'array':
        test = Array.isArray(data);
        break;
    case 'boolean':
        test = typeof data === 'boolean';
        break;
    case 'file':
        test = !options.modeSchema;
        break;
    case 'integer':
        test = Number.isFinite(data) && Math.floor(data) === data;
        switch (schema.format) {
        case 'int32':
            break;
        case 'int64':
            break;
        }
        break;
    case 'number':
        test = Number.isFinite(data);
        switch (schema.format) {
        case 'double':
            break;
        case 'float':
            break;
        }
        break;
    case 'string':
        test = typeof data === 'string' ||
            (!options.modeSchema && schema.format === 'binary');
        switch (test && !options.modeSchema && schema.format) {
        // Clarify 'byte' format #50
        // https://github.com/swagger-api/swagger-spec/issues/50
        case 'byte':
            test = !(/[^\n\r\+\/0-9\=A-Za-z]/).test(data);
            break;
        case 'date':
        case 'date-time':
            test = JSON.stringify(new Date(data)) !== 'null';
            break;
        case 'email':
            test = local.regexpEmailValidate.test(data);
            break;
        case 'json':
            test = local.tryCatchOnError(function () {
                JSON.parse(data);
                return true;
            }, local.nop);
            break;
        case 'phone':
            test = local.regexpPhoneValidate.test(data);
            break;
        }
        break;
    default:
        test = options.modeSchema || typeof data === 'object';
        break;
    }
    local.throwSwaggerError(!test && {
        data: data,
        errorType: 'itemType',
        prefix: options.prefix,
        schema: schema,
        typeof: typeof data
    });
    tmp = typeof data;
    if (tmp === 'object' && Array.isArray(data)) {
        tmp = 'array';
    }
    switch (tmp) {
    // 5.1. Validation keywords for numeric instances (number and integer)
    case 'number':
        // 5.1.1. multipleOf
        test = typeof schema.multipleOf !== 'number' || data % schema.multipleOf === 0;
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'numberMultipleOf',
            prefix: options.prefix,
            schema: schema
        });
        // 5.1.2. maximum and exclusiveMaximum
        test = typeof schema.maximum !== 'number' || (schema.exclusiveMaximum
            ? data < schema.maximum
            : data <= schema.maximum);
        local.throwSwaggerError(!test && {
            data: data,
            errorType: schema.exclusiveMaximum
                ? 'numberExclusiveMaximum'
                : 'numberMaximum',
            prefix: options.prefix,
            schema: schema
        });
        // 5.1.3. minimum and exclusiveMinimum
        test = typeof schema.minimum !== 'number' || (schema.exclusiveMinimum
            ? data > schema.minimum
            : data >= schema.minimum);
        local.throwSwaggerError(!test && {
            data: data,
            errorType: schema.exclusiveMinimum
                ? 'numberExclusiveMinimum'
                : 'numberMinimum',
            prefix: options.prefix,
            schema: schema
        });
        break;
    // 5.2. Validation keywords for strings
    case 'string':
        // 5.2.1. maxLength
        test = typeof schema.maxLength !== 'number' || data.length <= schema.maxLength;
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'stringMaxLength',
            prefix: options.prefix,
            schema: schema
        });
        // 5.2.2. minLength
        test = typeof schema.minLength !== 'number' || data.length >= schema.minLength;
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'stringMinLength',
            prefix: options.prefix,
            schema: schema
        });
        // 5.2.3. pattern
        test = !schema.pattern || new RegExp(schema.pattern).test(data);
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'stringPattern',
            prefix: options.prefix,
            schema: schema
        });
        break;
    // 5.3. Validation keywords for arrays
    case 'array':
        // 5.3.1. additionalItems and items
        // swagger disallows array items
        data.forEach(function (element, ii) {
            // recurse - schema.additionalItems and schema.items
            local.validateBySwaggerSchema({
                data: element,
                dataReadonlyRemove: [dataReadonlyRemove2, ii, dataReadonlyRemove2[ii]],
                modeSchema: options.modeSchema,
                prefix: options.prefix.concat([ii]),
                schema: local.schemaPItems(schema) || schema.additionalItems,
                swaggerJson: options.swaggerJson
            });
        });
        // 5.3.2. maxItems
        test = typeof schema.maxItems !== 'number' || data.length <= schema.maxItems;
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'arrayMaxItems',
            prefix: options.prefix,
            schema: schema
        });
        // 5.3.3. minItems
        test = typeof schema.minItems !== 'number' || data.length >= schema.minItems;
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'arrayMinItems',
            prefix: options.prefix,
            schema: schema
        });
        // 5.3.4. uniqueItems
        test = !schema.uniqueItems || data.every(function (element) {
            tmp = element;
            return data.indexOf(element) === data.lastIndexOf(element);
        });
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'arrayUniqueItems',
            prefix: options.prefix,
            schema: schema,
            tmp: tmp
        });
        break;
    // 5.4. Validation keywords for objects
    case 'object':
        // 5.4.1. maxProperties
        test = typeof schema.maxProperties !== 'number' ||
            Object.keys(data).length <= schema.maxProperties;
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'objectMaxProperties',
            prefix: options.prefix,
            schema: schema
        });
        // 5.4.2. minProperties
        test = typeof schema.minProperties !== 'number' ||
            Object.keys(data).length >= schema.minProperties;
        local.throwSwaggerError(!test && {
            data: data,
            errorType: 'objectMinProperties',
            prefix: options.prefix,
            schema: schema
        });
        // 5.4.3. required
        local.normalizeValue('list', schema.required).forEach(function (key) {
            test = !local.isNullOrUndefined(data[key]);
            local.throwSwaggerError(!test && {
                data: data,
                errorType: 'objectRequired',
                key: key,
                prefix: options.prefix,
                schema: schema
            });
        });
        // 5.4.4. additionalProperties, properties and patternProperties
        Object.keys(data).forEach(function (key) {
            tmp = null;
            if (schema.properties && schema.properties[key]) {
                tmp = true;
                // recurse - schema.properties
                local.validateBySwaggerSchema({
                    data: data[key],
                    dataReadonlyRemove: [
                        dataReadonlyRemove2,
                        key,
                        dataReadonlyRemove2[key]
                    ],
                    modeSchema: options.modeSchema,
                    prefix: options.prefix.concat([key]),
                    schema: schema.properties[key],
                    swaggerJson: options.swaggerJson
                });
            }
            Object.keys(schema.patternProperties || {}).forEach(function (rgx) {
                if (new RegExp(rgx).test(key)) {
                    tmp = true;
                    // recurse - schema.patternProperties
                    local.validateBySwaggerSchema({
                        data: data[key],
                        modeSchema: options.modeSchema,
                        prefix: options.prefix.concat([key]),
                        schema: schema.patternProperties[rgx],
                        swaggerJson: options.swaggerJson
                    });
                }
            });
/*
* validate
* 5.4.4.4. If "additionalProperties" has boolean value false
*
* In this case, validation of the instance depends on the property set of
* "properties" and "patternProperties". In this section, the property names of
* "patternProperties" will be called regexes for convenience.
*
* The first step is to collect the following sets:
*
* s
* The property set of the instance to validate.
* p
* The property set from "properties".
* pp
* The property set from "patternProperties".
* Having collected these three sets, the process is as follows:
*
* remove from "s" all elements of "p", if any;
* for each regex in "pp", remove all elements of "s" which this regex matches.
* Validation of the instance succeeds if, after these two steps, set "s" is empty.
*/
            test = tmp || schema.additionalProperties !== false;
            local.throwSwaggerError(!test && {
                data: data,
                errorType: 'objectAdditionalProperties',
                key: key,
                prefix: options.prefix,
                schema: schema
            });
            // recurse - schema.additionalProperties
            local.validateBySwaggerSchema({
                data: data[key],
                modeSchema: options.modeSchema,
                prefix: options.prefix.concat([key]),
                schema: schema.additionalProperties,
                swaggerJson: options.swaggerJson
            });
        });
        // 5.4.5. dependencies
        Object.keys(schema.dependencies || {}).forEach(function (key) {
            if (local.isNullOrUndefined(data[key])) {
                return;
            }
            // 5.4.5.2.1. Schema dependencies
            // recurse - schema.dependencies
            local.validateBySwaggerSchema({
                data: data[key],
                modeSchema: options.modeSchema,
                prefix: options.prefix.concat([key]),
                schema: schema.dependencies[key],
                swaggerJson: options.swaggerJson
            });
            // 5.4.5.2.2. Property dependencies
            local.normalizeValue('list', schema.dependencies[key]).every(function (key2) {
                test = !local.isNullOrUndefined(data[key2]);
                local.throwSwaggerError(!test && {
                    data: data,
                    errorType: 'objectDependencies',
                    key: key,
                    key2: key2,
                    prefix: options.prefix,
                    schema: schema
                });
            });
        });
        break;
    }
    // 5.5. Validation keywords for any instance type
    // 5.5.1. enum
    tmp = schema.enum || (!options.modeSchema && (local.schemaPItems(schema) || {}).enum);
    test = !tmp || (Array.isArray(data)
        ? data
        : [data]).every(function (element) {
        return tmp.indexOf(element) >= 0;
    });
    local.throwSwaggerError(!test && {
        data: data,
        errorType: 'itemEnum',
        prefix: options.prefix,
        schema: schema,
        tmp: tmp
    });
    // 5.5.2. type
    local.nop();
    // 5.5.3. allOf
    (schema.allOf || []).forEach(function (element) {
        // recurse - schema.allOf
        local.validateBySwaggerSchema({
            data: data,
            prefix: options.prefix,
            modeSchema: options.modeSchema,
            schema: element,
            swaggerJson: options.swaggerJson
        });
    });
    // 5.5.4. anyOf
    tmp = null;
    test = !schema.anyOf || schema.anyOf.some(function (element) {
        local.tryCatchOnError(function () {
            // recurse - schema.anyOf
            local.validateBySwaggerSchema({
                data: data,
                modeSchema: options.modeSchema,
                prefix: options.prefix,
                schema: element,
                swaggerJson: options.swaggerJson
            });
            return true;
        }, local.nop);
        tmp = tmp || local.utility2._debugTryCatchError;
        return !tmp;
    });
    local.throwSwaggerError(!test && {
        data: data,
        errorType: 'itemOneOf',
        prefix: options.prefix,
        schema: schema,
        tmp: tmp
    });
    // 5.5.5. oneOf
    tmp = !schema.oneOf
        ? 1
        : 0;
    (schema.oneOf || []).some(function (element) {
        local.tryCatchOnError(function () {
            // recurse - schema.oneOf
            local.validateBySwaggerSchema({
                data: data,
                modeSchema: options.modeSchema,
                prefix: options.prefix,
                schema: element,
                swaggerJson: options.swaggerJson
            });
            tmp += 1;
        }, local.nop);
        return tmp > 1;
    });
    test = tmp === 1;
    local.throwSwaggerError(!test && {
        data: data,
        errorType: 'itemOneOf',
        prefix: options.prefix,
        schema: schema,
        tmp: tmp
    });
    // 5.5.6. not
    test = !schema.not || !local.tryCatchOnError(function () {
        // recurse - schema.not
        local.validateBySwaggerSchema({
            data: data,
            modeSchema: options.modeSchema,
            prefix: options.prefix,
            schema: schema.not,
            swaggerJson: options.swaggerJson
        });
        return true;
    }, local.nop);
    local.throwSwaggerError(!test && {
        data: data,
        errorType: 'itemNot',
        prefix: options.prefix,
        schema: schema
    });
    // 5.5.7. definitions
    local.nop();
    // validate data.$ref
    if (schema === local.swaggerSchemaJson.definitions.jsonReference) {
        local.validateBySwaggerSchema({
            modeDereference: true,
            modeSchema: options.modeSchema,
            prefix: options.prefix,
            schema: data,
            swaggerJson: options.swaggerJson
        });
    }
    return schema;
};

/*
 * output from running code inside browser-console
 */
var mySchema = {
    required: ['myBoolean'],
    properties: {
        myArrayOfStrings: { items: { type: 'string' }, type: 'array' },
        myBoolean: { type: 'boolean' },
        myNumber: { type: 'number' },
        myString: { enum: ['hello world', 'bye world'], type: 'string' }
    }
};

undefined

local.validateBySwaggerSchema({
    data: {
        myArrayOfStrings: ['foo', 'bar'],
        myBoolean: false,
        myString: 'hello world'
    },
    prefix: ['myData'],
    schema: mySchema
});

{required: Array(1), properties: {…}}

local.validateBySwaggerSchema({
    data: {
        myArrayOfStrings: [1, 2],
        myBoolean: false
    },
    prefix: ['myData'],
    schema: mySchema
});

assets.utility2.rollup.js:26060 Uncaught Error: error.itemType - value myData["myArrayOfStrings"][0] = 1 is not a valid string
    at Object.local.throwSwaggerError (assets.utility2.rollup.js:26048)
    at Object.local.validateBySwaggerSchema (assets.utility2.rollup.js:27017)
    at assets.utility2.rollup.js:27097
    at Array.forEach (<anonymous>)
    at Object.local.validateBySwaggerSchema (assets.utility2.rollup.js:27095)
    at assets.utility2.rollup.js:27172
    at Array.forEach (<anonymous>)
    at Object.local.validateBySwaggerSchema (assets.utility2.rollup.js:27167)
    at <anonymous>:2:7

local.throwSwaggerError @ assets.utility2.rollup.js:26048
local.validateBySwaggerSchema @ assets.utility2.rollup.js:27017
(anonymous) @ assets.utility2.rollup.js:27097
local.validateBySwaggerSchema @ assets.utility2.rollup.js:27095
(anonymous) @ assets.utility2.rollup.js:27172
local.validateBySwaggerSchema @ assets.utility2.rollup.js:27167
(anonymous) @ VM341:2

local.validateBySwaggerSchema({
    data: {
        myArrayOfStrings: ['foo', 'bar'],
        myBoolean: null
    },
    prefix: ['myData'],
    schema: mySchema
});

assets.utility2.rollup.js:26060 Uncaught Error: error.objectRequired - object myData = {"myArrayOfStrings":["foo","bar"],"myBoolean":null} must have property "myBoolean"
    at Object.local.throwSwaggerError (assets.utility2.rollup.js:26048)
    at assets.utility2.rollup.js:27158
    at Array.forEach (<anonymous>)
    at Object.local.validateBySwaggerSchema (assets.utility2.rollup.js:27156)
    at <anonymous>:2:7

local.throwSwaggerError @ assets.utility2.rollup.js:26048
(anonymous) @ assets.utility2.rollup.js:27158
local.validateBySwaggerSchema @ assets.utility2.rollup.js:27156
(anonymous) @ VM343:2

local.validateBySwaggerSchema({
    data: {
        myBoolean: false,
        myString: 'hello undefined'
    },
    prefix: ['myData'],
    schema: mySchema
});

assets.utility2.rollup.js:26060 Uncaught Error: error.itemEnum - string myData["myString"] = "hello undefined" can only have items from the list ["hello world","bye world"]
    at Object.local.throwSwaggerError (assets.utility2.rollup.js:26048)
    at Object.local.validateBySwaggerSchema (assets.utility2.rollup.js:27274)
    at assets.utility2.rollup.js:27172
    at Array.forEach (<anonymous>)
    at Object.local.validateBySwaggerSchema (assets.utility2.rollup.js:27167)
    at <anonymous>:2:7

local.throwSwaggerError @ assets.utility2.rollup.js:26048
local.validateBySwaggerSchema @ assets.utility2.rollup.js:27274
(anonymous) @ assets.utility2.rollup.js:27172
local.validateBySwaggerSchema @ assets.utility2.rollup.js:27167
(anonymous) @ VM345:2
# Jerry Schulteis (6 years ago)

No if / else if / else or nested if blocks:


// Simulated fallible fetchData
const fetchData = () => {
  const x = Math.random();
  if (x < 0.25) return {error: true, message: "fetchData error"};
  if (x < 0.50) return {};
  if (x < 0.75) return {data: {}};
  return {data: {todos: ["refactor your code"]}};
};

// Simulated action
const action = o => log(o.data);
    
const checkForError = response => {
  const isError = response.error;
  if (isError) {
    log(response.message);
  }
  return isError;
};

const checkForNoData = response => {
  const isNoData = !response.data;
  if (isNoData) {
    log("No data");
  }
  return isNoData;
};

const checkForNoTodos = response => {
  const isNoTodos = !response.data.todos;
  if (isNoTodos) {
    log("No Todos");
  }
  return isNoTodos;
};

const checkList = [
  checkForError,
  checkForNoData,
  checkForNoTodos
];

const doWork = () => {
  const response = fetchData();
  const checkFailed = checkList.find(check => check(response));
  if (!checkFailed) {
    return action({data: response.data});
  }
};
# Naveen Chawla (6 years ago)

Kai, it's unlikely anyone will read all that code! Better to illustrate your point with minimal code snippets

# Isiah Meadows (6 years ago)

As previously mentioned, this is effectively already covered by labeled blocks. They aren't slated for removal (some things are just nearly impossible to express without them), just they have a tendency to get abused like goto in C - people have this tendency in C/C++ to get a little over-eager to reach for that shotgun when a while/for/if/etc. would do. So a strong discouragement is generally warranted, but mainly to get less-skilled coders to not code themselves into an unmaintainable hole before they can properly wield the tool and understand why it exists. (It makes for a nice way to break from non-trivial logic when a separate function would only serve to complicate the code.)

Isiah Meadows me at isiahmeadows.com

Looking for web consulting? Or a new website? Send me an email and we can get started. www.isiahmeadows.com