簡體   English   中英

具有Typescript和ES6代理的MongoModel類

[英]MongoModel Class with Typescript and ES6 Proxy

我想在打字稿中實現MongoModel類。 類似於php或rails中的ActiveRecord。

class Model {
    data: any; 
}
let model = new Model();

我要完成的是

代替model.data.name = 'John' ,我更喜歡model.name = 'John' 更復雜的是該字段應該是靈活的(就像mongo一樣)。 model['anything'] = 'can be assigned'將最終在data屬性中分配。 將與model.data['anything'] = 'can be anything'

我嘗試了defineProperty,但是它不能處理所有的setter / getters。 我已經嘗試了代理,但是不確定如何包裝Model類代理。

嗯,我不知道“ MongoModel”是什么,也不知道應該是BasecollectionNamedocId是什么。 因此,如果以下內容與您的期望不完全匹配並且您無法適應它,則可能需要考慮編輯代碼以構成一個最小的可復制示例 但是,讓我們看看我們可以在這里做什么。

首先,讓我們將Model重命名為InnerModel ,該Model將用於實現所需的類型,但不會按原樣公開給用戶:

class InnerModel<T> {
  constructor(public data: T) {}
  doSave(): boolean {
    console.log("saving");
    return true; // implement me
  }
}

請注意,我為它提供了一個構造函數來保存T類型的數據。 現在的目標是使一類像Model<T>其中既用作TInnerModel<T>

type Model<T> = T & InnerModel<T>;

如果您有Model<T>實例,則可以直接訪問T所有屬性以及InnerModel<T>所有屬性。 請注意,如果T具有與InnerModel相同的某些屬性名稱,我將完全忽略發生的情況。 如果T{data: number, doSave: boolean}那將很糟糕。 所以不要那樣做。

無論如何,這里的目標是制作一些實際上構造Model<T>實例的東西。

請注意,編譯器無法真正驗證您從頭開始所做的一切都是類型安全的,因此您將需要使用類型斷言或等效聲明來防止編譯器發出錯誤。 這意味着您必須謹慎,只聲明自己可以驗證為真實的內容。

首先,我們將添加一個幫助程序類型防護函數,以幫助區分屬性名稱是否是對象的已知鍵...我們將在下一步中使用它來幫助編譯器了解該屬性鍵是在InnerModel本身上還是在嵌套data屬性:

function hasKey<T extends object>(obj: T, k: keyof any): k is keyof T {
  return k in obj;
}

這是實現的主要部分...使用代理將屬性獲取/設置路由到模型或數據,具體取決於在哪里找到密鑰

function makeModel<T>(data: T): Model<T> {
  return new Proxy(new InnerModel(data), {
    get(model: InnerModel<T>, prop: keyof Model<T>) {
      return hasKey(model, prop) ? model[prop] : model.data[prop];
    },
    set(model: InnerModel<T>, prop: keyof Model<T>, value: any) {
      return hasKey(model, prop)
        ? (model[prop] = value)
        : (model.data[prop] = value);
    }
  }) as Model<T>;
}

在某些地方這不是類型安全的... get()set()處理程序返回any ,並且在set()處理程序set() value鍵入any 這通常會關閉類型檢查,因此我們需要手動檢查正確性。 而且編譯器看不到我們正在返回Model<T>而不是InnerModel<T> ,因此我們需要斷言。

最后,我們將使用該makeModel()函數並將其視為構造函數。 在JavaScript中,您可以將任何函數用作構造函數 ,如果該函數返回值,則構造的對象將是該返回值。 編譯器確實不喜歡我們這樣做,因此需要雙重聲明:

const Model = makeModel as unknown as new <T>(data: T) => Model<T>;

但是現在我們有了一些可行的方法:

const n = new Model({
  a: "hey",
  b: 1,
  c: true,
  d() {
    console.log("D");
  }
});

console.log(n.a); // hey
n.a = "you";
console.log(n.a); // you
console.log(n.data.a); // you
n.d(); // D
n.doSave(); // saving

好的,希望對您有所幫助。 祝好運!

鏈接到代碼

編輯:如果您希望它“靈活”,則應通過執行諸如new Model<{[k: string]: any}>({})類的操作使T “靈活”,但是模型越靈活,則越少是強類型的。 是否要使用可索引類型取決於您自己,但實際上並不會影響上面的實現(反正影響不大)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM