简体   繁体   English

javascript中的结构类型检查

[英]structural type checking in javascript

I'm wondering if there is a common way, perhaps a library, to check the structure of objects (like duck typing).我想知道是否有一种通用的方法,也许是一个库,来检查对象的结构(比如鸭子类型)。

This can be useful for both runtime type-checking, and for writing unit tests.这对于运行时类型检查和编写单元测试都很有用。

I think I'm looking for something similar to typescript "interfaces', bu typescript only does static checking.我想我正在寻找类似于打字稿“接口”的东西,但打字稿只进行静态检查。

No simple way, but how about a utility function?:没有简单的方法,但是实用函数怎么样?:

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

So basically, you can compare any object to a model with this.所以基本上,您可以将任何对象与模型进行比较。 It will make sure the object has all the properties the model has, of the same types, and recursively apply that to nested objects.它将确保对象具有模型具有的所有属性,属于相同类型,并递归地将其应用于嵌套对象。

Even with Typescript interfaces, there isn't an easy comparison to make sure the structure of a object matches type.即使使用 Typescript 接口,也无法通过简单的比较来确保对象的结构与类型匹配。 For sanity checking, I've used a conditional operator to check all needed properties of the object:对于健全性检查,我使用了条件运算符来检查对象的所有必需属性:

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
}

UPDATE: If you want duck typing as defined as https://en.wikipedia.org/wiki/Duck_typing then check-types.js https://gitlab.com/philbooth/check-types.js has you covered.更新:如果你想要鸭子类型定义为https://en.wikipedia.org/wiki/Duck_typing然后 check-types.js https://gitlab.com/philbooth/check-types.js已经涵盖了。 See check.like(...) Additionally, based on the wiki article IceMetalPunk's solution also holds up.请参阅 check.like(...) 此外,基于 wiki 文章 IceMetalPunk 的解决方案也成立。

Additionally, I've created a codesandbox: https://codesandbox.io/embed/optimistic-tu-d8hul?expanddevtools=1&fontsize=14&hidenavigation=1此外,我创建了一个代码和框: https ://codesandbox.io/embed/optimistic-tu-d8hul ? expanddevtools =1& fontsize =14& hidenavigation =1

Original answer, not exactly correct: "Going to have to give this a soft "no". There is no way to 100% know that Object A is structurally an unmodified object of class X. If you are less strict then the answer would be a soft "yes". If you are looking to compare A to B then you can compare props. Again, though you could have a case where both A and B come from the same parent class X and have not been mutated by any outside force other than calling the objects own function."原始答案,不完全正确:“不得不给这个软性的“不”。没有办法 100% 知道对象 A 在结构上是 X 类的未修改对象。如果你不那么严格,那么答案是一个柔和的“是”。如果您想将 A 与 B 进行比较,那么您可以比较道具。同样,尽管您可能会遇到 A 和 B 来自同一个父类 X 并且没有被任何外部力量变异的情况除了调用对象自己的函数。”

Borrowing a function from MDN to get started.从 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;
}

This function will check if our Object A is effectively the same as our base object created from class X.这个函数将检查我们的对象 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)
}

Now let's create a couple of test classes.现在让我们创建几个测试类。

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
}

Create some new test objects创建一些新的测试对象

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)

For our first set of test, we will show that our function "isPureObject" can correctly test that A is an object of class X and has not been mutated outside the starting template.对于我们的第一组测试,我们将展示我们的函数“isPureObject”可以正确测试 A 是类 X 的对象并且没有在起始模板之外发生变异。 I've also included IceMetalPunk's function isOfType and check.like for comparison.我还包括了 IceMetalPunk 的函数 isOfType 和 check.like 以进行比较。

Test basic cases where the test object "is" a duck that has not been mutated.测试测试对象“是”未变异的鸭子的基本情况。

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

Test cases where the test object "is" a mutated duck.测试对象“是”变异鸭子的测试用例。

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

Test cases where the test object "is like" a duck but not a duck.测试对象“像”一只鸭子而不是一只鸭子的测试用例。

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

And lastly, we show why this is such a hard problem by having the object under test mutate in an intended way and make isPureObject function fail.最后,我们通过让被测对象以预期的方式发生变异并使 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)}`)

Original answer: "Again, I can't give this a hard no or a hard yes as I suspect there are ways this could be done using an object.constructor.toSting() to compare against the objects current state but even that may not be enough. I also know that React.js does something along these lines but they may be doing it against very specific objects/class whereas I assume you are looking for a broad general uses case."原始答案:“同样,我不能给出一个严格的否或一个严格的是,因为我怀疑有一些方法可以使用 object.constructor.toSting() 来与对象当前状态进行比较,但即使这样也可能不会足够了。我也知道 React.js 会按照这些方式做一些事情,但他们可能是针对非常特定的对象/类做的,而我假设您正在寻找广泛的通用用例。”

Updated: It really depends on what you want to do.更新:这真的取决于你想做什么。 If you are looking for duck typing then there are many solutions.如果您正在寻找鸭子打字,那么有很多解决方案。 A few have been covered here.这里已经介绍了一些。 If you are looking for a structural/unmutated object then isPureObject will handle that.如果您正在寻找结构化/未突变的对象,那么 isPureObject 将处理它。 However, it will fall short on objects that can self-mutate.但是,它不能用于可以自我变异的对象。

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

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