【问题标题】:Should I manually validate parameters in TypeScirpt if create the library?如果创建库,我应该手动验证 TypeScript 中的参数吗?
【发布时间】:2022-11-10 01:02:26
【问题描述】:

至少大致了解 TypeScript 是如何工作的,每个人都可以回答:“是的。TypeScript 只是将代码转换为 JavaScript,但没有添加任何额外的行为,包括执行期间的类型检查。”例如,

function example(parameter: string): void {
  console.log(parameter.charAt(1));
}

将被转译为:

"use strict";
function example(parameter) {
  console.log(parameter.charAt(1));
}

如果 JavaScript 用户调用example(3),则会抛出错误Uncaught TypeError: parameter.charAt is not a function

好吧,如果我明白这一点,为什么我要问这个问题?因为我要花几十个小时来改进我的库(@yamato-daiwa/es-extensions),但更重要的是,将库大小增加了数倍。这并不夸张。例如,对于 1.6.x 版本,addElementsToArray 函数非常简单:

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;
}

一旦我通过类型检查对其进行了改进,它就变成了:

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;

}

在这种情况下,验证需要大约一半的行,并且还出现了很多依赖项。有时,验证可以超过 90% 的功能线。可分发的库将变得更重,这对于每千字节都在计数的前端应用程序至关重要。

【问题讨论】:

  • 您可以执行验证,也可以期望使用该库的每个人都将以类型感知的方式使用它。 (如果我使用与图书馆文档所说的不同的调用签名,我完全希望我使用的图书馆会破坏 - 那就是我的错,不是图书馆的错,IMO)
  • 我的意思是,它不仅仅是在 TypeScript 中。如果您使用 JavaScript 编写它,则必须进行相同的验证。

标签: typescript


【解决方案1】:

在系统面向客户的边缘进行验证是一种良好且常见的做法。

一开始所有那些清嗓子的代码都可以被视为一件好事,使这些函数的主体更加简单和自信地编写。

您当然也可以通过查找或构建断言库来缩减代码。

【讨论】:

    猜你喜欢
    • 2017-10-28
    • 2013-02-17
    • 1970-01-01
    • 1970-01-01
    • 2010-12-15
    • 1970-01-01
    • 1970-01-01
    • 2021-06-13
    • 1970-01-01
    相关资源
    最近更新 更多