簡體   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