[英]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”是什么,也不知道應該是Base
, collectionName
和docId
是什么。 因此,如果以下內容與您的期望不完全匹配並且您無法適應它,則可能需要考慮編輯代碼以構成一個最小的可復制示例 。 但是,讓我們看看我們可以在這里做什么。
首先,讓我們將Model
重命名為InnerModel
,該Model
將用於實現所需的類型,但不會按原樣公開給用戶:
class InnerModel<T> {
constructor(public data: T) {}
doSave(): boolean {
console.log("saving");
return true; // implement me
}
}
請注意,我為它提供了一個構造函數來保存T
類型的數據。 現在的目標是使一類像Model<T>
其中既用作T
和InnerModel<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.