【问题标题】:structural type checking in javascriptjavascript中的结构类型检查
【发布时间】:2019-07-19 19:13:59
【问题描述】:

我想知道是否有一种通用的方法,也许是一个库,来检查对象的结构(如鸭子类型)。

这对于运行时类型检查和编写单元测试都很有用。

我想我正在寻找类似于 typescript "interfaces" 的东西,但 typescript 只做静态检查。

【问题讨论】:

    标签: javascript ecmascript-6


    【解决方案1】:

    没有简单的方法,但是实用函数怎么样?:

    function isOfType(obj, model) {
      for (let prop in model) {
        if (!(prop in obj) || typeof obj[prop] !== typeof model[prop] || Array.isArray(model[prop]) !== Array.isArray(obj[prop])) {
          return false;
        }
        if (typeof model[prop] === 'object' && !Array.isArray(model[prop])) {
          if (!isOfType(obj[prop], model[prop])) {
            return false;
          }
        }
      }
      return true;
    }
    

    所以基本上,您可以将任何对象与模型进行比较。它将确保对象具有模型所具有的所有属性、相同类型,并递归地将其应用于嵌套对象。

    【讨论】:

      【解决方案2】:

      更新:如果你想要鸭子类型定义为https://en.wikipedia.org/wiki/Duck_typing,那么 check-types.js https://gitlab.com/philbooth/check-types.js 已经涵盖了。请参阅 check.like(...) 此外,基于 wiki 文章 IceMetalPunk 的解决方案也成立。

      另外,我创建了一个代码框:https://codesandbox.io/embed/optimistic-tu-d8hul?expanddevtools=1&fontsize=14&hidenavigation=1

      原始答案,不完全正确:“必须给这个软“不”。没有办法 100% 知道对象 A 在结构上是 X 类的未修改对象。如果你不那么严格,那么答案将是一个柔和的“是”。如果您想比较 A 和 B,那么您可以比较道具。同样,尽管您可能会遇到 A 和 B 都来自同一个父类 X 并且没有被除了调用对象自己的函数之外的任何外力。”

      从 MDN 借用一个函数来开始。

      // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Working_with_Objects
      function listAllProperties(o) {
          var objectToInspect;
          var result = [];
      
          for(objectToInspect = o; objectToInspect !== null; objectToInspect = Object.getPrototypeOf(objectToInspect)) {
              result = result.concat(Object.getOwnPropertyNames(objectToInspect));
          }
      
          return result;
      }
      

      此函数将检查我们的对象 A 是否实际上与从类 X 创建的基础对象相同。

      function isPureObject(baseObj, objToCheck) {
          // instanceof to make sure we don't have a object that has the same starting definition but is actually different class
          if (!(objToCheck instanceof baseObj.constructor)) return false
          let baseProps = listAllProperties(baseObj)
          return listAllProperties(objToCheck).every(prop => baseProps.indexOf(prop) > -1)
      }
      

      现在让我们创建几个测试类。

      class Test {
          constructor(b) { this.b = b }
          a() { this.d = 18}
          c = 5
      }
      
      // this is effective the same as Test but is a different class
      class LikeTest {
          constructor(b) { this.b = b }
          a() { this.d = 18 }
          c = 5
      }
      

      创建一些新的测试对象

      let a = new Test(3)
      let b = new Test(42)
      let c = new Test(42)
      let likeTest = new LikeTest(3)
      c.z = 10
      
      let base = new Test(0)
      

      对于我们的第一组测试,我们将证明我们的函数“isPureObject”可以正确地测试 A 是 X 类的对象并且没有在起始模板之外发生变异。我还加入了 IceMetalPunk 的函数 isOfType 和 check.like 进行比较。

      测试测试对象“是”尚未变异的鸭子的基本情况。

      console.log(`Test basic cases where the test object "is" a duck that has not been mutated.`);
      console.log(`------------------------------------------------------------`);
      console.log(`expect true - isPureObject(base, a) = ${isPureObject(base, a)}`);
      console.log(`expect true - isOfType(a, base)     = ${isOfType(a, base)}`);
      console.log(`expect true - check.like(a, base)   = ${check.like(a, base)}`);
      console.log(`expect true - isPureObject(base, b) = ${isPureObject(base, b)}`);
      console.log(`expect true - isOfType(b, base)     = ${isOfType(b, base)}`);
      console.log(`expect true - check.like(b, base)   = ${check.like(b, base)}`);
      

      测试对象“是”变异鸭子的测试用例。

      console.log(`\n\nTest cases where the test object "is" a mutated duck.`);
      console.log(`------------------------------------------------------------`);
      console.log(`expect false - isPureObject(base, c) = ${isPureObject(base, c)}`);
      console.log(`expect true  - isOfType(c, base)     = ${isOfType(c, base)}`);
      console.log(`expect true  - check.like(c, base)   = ${check.like(c, base)}`);
      

      测试对象“像”鸭子但不是鸭子的测试用例。

      console.log(`\n\nTest cases where the test object "is like" a duck but not a duck.`);
      console.log(`------------------------------------------------------------`);
      console.log(`expect false - isPureObject(base, likeTest) = ${isPureObject(base,likeTest)}`);
      console.log(`expect true  - isOfType(likeTest, base)     = ${isOfType(likeTest, base)}`);
      console.log(`expect true  - check.like(likeTest, base)   = ${check.like(likeTest, base)}`);
      

      最后,我们通过让被测对象以预期方式发生变异并使 isPureObject 函数失败来说明为什么这是一个如此棘手的问题。

      a.a();
      console.log('\n\nCalled a.a() which sets this.d to a value that was not previously defined.')
      console.log(`------------------------------------------------------------`);
      console.log(`expect true - isPureObject(base, a) after calling a.a() = ${isPureObject(base, a)}`)
      console.log(`expect true - isOfType(a, base) after calling a.a()     = ${isOfType(a, base)}`)
      console.log(`expect true - check.like(a, base) after calling a.a()   = ${check.like(a, base)}`)
      

      原始答案:“同样,我不能给出一个硬性的否定或肯定的答案,因为我怀疑有一些方法可以使用 object.constructor.toSting() 来与对象的当前状态进行比较,但即使这可能还不够。我也知道 React.js 在这些方面做了一些事情,但他们可能会针对非常特定的对象/类来做这件事,而我假设你正在寻找一个广泛的通用用例。”

      更新:这真的取决于你想做什么。如果您正在寻找鸭子打字,那么有很多解决方案。这里已经介绍了一些。如果您正在寻找结构/未变异的对象,那么 isPureObject 将处理它。但是,它会在可以自我变异的对象上有所不足。

      【讨论】:

        【解决方案3】:

        即使使用 Typescript 接口,也没有简单的比较来确保对象的结构与类型匹配。对于完整性检查,我使用了条件运算符来检查对象的所有需要​​的属性:

        yourObject = {
          name: 'cw';
          work: {
            employed: true;
            company: 'stackoverflow'
          }
        }
        if (yourObject &&
          yourObject.hasOwnProperty('name') &&
          yourObject.hasOwnProperty('work') &&
          yourObject.work.hasOwnProperty('employed') &&
          yourObject.work.hasOwnProperty('company')
        ) {
          //structure === good
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-29
          • 1970-01-01
          • 2020-04-14
          • 1970-01-01
          • 2018-04-05
          • 1970-01-01
          相关资源
          最近更新 更多