繁体   English   中英

javascript中的结构类型检查

[英]structural type checking in javascript

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

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

我想我正在寻找类似于打字稿“接口”的东西,但打字稿只进行静态检查。

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

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

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

即使使用 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
}

更新:如果你想要鸭子类型定义为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 将处理它。 但是,它不能用于可以自我变异的对象。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM