Issue
Knowing at least cursorily how TypeScript works, everyone can answer: "Yes. The TypeScript is just transpiling the code to JavaScript but not adding any extra behavior including the type checking during execution." For example,
function example(parameter: string): void {
console.log(parameter.charAt(1));
}
will be transpiled to:
"use strict";
function example(parameter) {
console.log(parameter.charAt(1));
}
And if JavaScript user will call example(3)
, the error Uncaught TypeError: parameter.charAt is not a function
will be thrown.
Well, if I understand this why I as asking this question? Because I am about to spend a tens of hours for improvement of my library (@yamato-daiwa/es-extensions), but, more importantly, increase the library size by multiple times. It is not the exaggeration. For example, for the version 1.6.x, the addElementsToArray function is pretty simple:
export default function addElementsToArray<ArrayElement>(
namedParameters:
{
targetArray: Array<ArrayElement>;
newElements: Array<ArrayElement>;
mutably: boolean;
} &
(
{ toStart: true; } |
{ toEnd: true; } |
{ toPosition__numerationFrom0: number; } |
{ toPosition__numerationFrom1: number; }
)
): Array<ArrayElement> {
const workpiece: Array<ArrayElement> = namedParameters.mutably ?
namedParameters.targetArray : [ ...namedParameters.targetArray ];
if ("toStart" in namedParameters) {
workpiece.unshift(...namedParameters.newElements);
return workpiece;
}
if ("toEnd" in namedParameters) {
workpiece.push(...namedParameters.newElements);
return workpiece;
}
const positionOfFirstNewElement__numerationFrom0: number = "toPosition__numerationFrom0" in namedParameters ?
namedParameters.toPosition__numerationFrom0 : namedParameters.toPosition__numerationFrom1 - 1;
workpiece.splice(positionOfFirstNewElement__numerationFrom0, 0, ...namedParameters.newElements);
return workpiece;
}
Once I have improved it with type checking, it has become to:
import Logger from "../Logging/Logger";
import InvalidParameterValueError from "../Errors/InvalidParameterValue/InvalidParameterValueError";
import IncompatiblePropertiesInObjectTypeParameterError from
"../Errors/IncompatiblePropertiesInObjectTypeParameter/IncompatiblePropertiesInObjectTypeParameterError";
import stringifyAndFormatArbitraryValue from "../Strings/stringifyAndFormatArbitraryValue";
import isArbitraryObject from "../TypeGuards/Objects/isArbitraryObject";
import isNotUndefined from "../TypeGuards/Nullables/isNotUndefined";
import isNonNegativeInteger from "../TypeGuards/Numbers/isNonNegativeInteger";
import isNaturalNumber from "../TypeGuards/Numbers/isNaturalNumber";
export default function addElementsToArray<ArrayElement>(
namedParameters:
Readonly<
(
{
mutably: true;
targetArray: Array<ArrayElement>;
} |
{
mutably: false;
targetArray: ReadonlyArray<ArrayElement>;
}
) &
{
newElements: ReadonlyArray<ArrayElement>;
toStart?: true;
toEnd?: true;
toPosition__numerationFrom0?: number;
toPosition__numerationFrom1?: number;
}
>
): Array<ArrayElement> {
if (!isArbitraryObject(namedParameters)) {
Logger.throwErrorAndLog({
errorInstance: new InvalidParameterValueError({
parameterNumber: 1,
parameterName: "namedParameters",
messageSpecificPart: "The first and only parameter of 'addElementsToArray' must be of the object type."
}),
title: InvalidParameterValueError.localization.defaultTitle,
occurrenceLocation: "addElementsToArray(namedParameters)"
});
}
if (!Array.isArray(namedParameters.targetArray)) {
Logger.throwErrorAndLog({
errorInstance: new InvalidParameterValueError({
parameterName: "namedParameters",
parameterNumber: 1,
messageSpecificPart: "The 'targetArray' is not the valid array and has value:\n" +
`${ stringifyAndFormatArbitraryValue(namedParameters.targetArray) }`
}),
title: InvalidParameterValueError.localization.defaultTitle,
occurrenceLocation: "addElementsToArray(namedParameters)"
});
}
const alternativelyRequestOptions: Array<true | number> = [
namedParameters.toStart,
namedParameters.toEnd,
namedParameters.toPosition__numerationFrom0,
namedParameters.toPosition__numerationFrom1
].filter(isNotUndefined);
if (alternativelyRequestOptions.length > 1) {
Logger.logError({
errorType: IncompatiblePropertiesInObjectTypeParameterError.NAME,
title: IncompatiblePropertiesInObjectTypeParameterError.localization.defaultTitle,
description: "Exactly one of 'toStart', 'toEnd', 'toPosition__numerationFrom0', 'toPosition__numerationFrom1' " +
"must be specified while actually multiple of them has been. No elements will be added to target array.",
occurrenceLocation: "addElementsToArray(namedParameters)"
});
return namedParameters.targetArray;
}
const workpiece: Array<ArrayElement> = namedParameters.mutably ?
namedParameters.targetArray : [ ...namedParameters.targetArray ];
if (namedParameters.toStart === true) {
workpiece.unshift(...namedParameters.newElements);
return workpiece;
}
if (namedParameters.toEnd === true) {
workpiece.push(...namedParameters.newElements);
return workpiece;
}
let positionOfFirstNewElement__numerationFrom0: number;
if (isNonNegativeInteger(namedParameters.toPosition__numerationFrom0)) {
positionOfFirstNewElement__numerationFrom0 = namedParameters.toPosition__numerationFrom0;
} else if (isNaturalNumber(namedParameters.toPosition__numerationFrom1)) {
positionOfFirstNewElement__numerationFrom0 = namedParameters.toPosition__numerationFrom1 - 1;
} else {
Logger.logError({
errorType: InvalidParameterValueError.NAME,
title: InvalidParameterValueError.localization.defaultTitle,
description: alternativelyRequestOptions.length === 0 ?
"None one of 'toStart', 'toEnd', 'toPosition__numerationFrom0', 'toPosition__numerationFrom1' has been specified." :
(
"The specified value of 'toStart', 'toEnd', 'toPosition__numerationFrom0' or 'toPosition__numerationFrom1' has " +
"invalid type, value or numbers set."
) +
"No elements will be added to target array.",
occurrenceLocation: "addElementsToArray(namedParameters)"
});
return namedParameters.targetArray;
}
workpiece.splice(positionOfFirstNewElement__numerationFrom0, 0, ...namedParameters.newElements);
return workpiece;
}
In this case, the validation takes about half of the lines and also a lot of dependencies has appeared. Sometimes, the validation could than 90% of function lines. The library distributable will become much heavier which is critical for the front-end application where each kilobyte is on count.
Potential falsy issues
In short, I could be reported by non-TypeScript users about bugs caused by invalid parameter's type, however if the same code has been written by the TypeScript, the TypeScript has emitted the error. Ever finally it will become clear that the issue was because of invalid parameter type in the combination with JavaScript usage instead of TypeScript, the time will be wasted by the investigations, conversations, etc. Also it could be the huge number of such issues which actually just the invalid TypeScript.
The update to the occasion of bounty
This question already has been answered, but the potential future increasing of the size of my libraries by multiple times because of parameters validation is bugging me. To exhaust this question, I need to be told either "You are doing the right thing, keep on it! The increasing of the distributive's size by the multiple time does not matter ever for the frontend application if it because of parameters validation." or "The large size of helper library is not good ever if it because of parameter's validation. You should skip the parameters validation to keep the library slim." by senior/master web developers.
Solution
It's a good and common practice to do validation at the customer-facing edge of your system.
All that throat-clearing code at the start can be seen as a good thing, making the bodies of those functions more simple and confidently written.
You can also certainly shrink the code by either finding or building an assertion library.
Answered By - danh
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.