我正在尝试在项目中使用 Dexie,并且尝试同时使用 Typescript 和
.mapToClass
功能。当尝试实现 Dexie: Typescript 中的示例时,我得到了一些混合结果。这是我的意思的一个例子:
模型/Entry.ts
export default abstract class Entry<T extends {}> {
constructor(props: T, hiddenPropKeys?: string[]) {
// merge props with instance to initialize all props from the interface/class
Object.assign(this, props);
// programmatically adding non-serializable keys
hiddenPropKeys?.forEach((key) =>
Object.defineProperty(this, key, { writable: true }),
);
}
}
模型/Friend.ts
import db from '../db';
import Entry from './Entry';
interface IFriend {
id?: number;
name: string;
relationship: string;
}
const table = db.friends;
type Type = IFriend; // shorthand, mainly for the static method
// doing this instead of having to iterate over every interface member again, as part of the class
interface Friend extends Type {
addresses: string[]
}
// eslint-disable-next-line no-redeclare
class Friend extends Entry<Type> {
constructor(params: Type) {
super(params, ['addresses']);
}
save() {
/* do some stuff to process the Friend */
return table.put(this);
}
getAddresses() {
/* some method to grab addresses and store them locally */
this.addresses = ['123 Main St', '55 Foo Bar Ln'];
}
// static helper basically do a db lookup
// I was thinking to add a caching layer here as well
// it won't stop you from attempting to search by non-indexed fields, but neither will Dexie
static find<T extends keyof Type>(
key: T,
value: Required<Type>[T] | Required<Type>[T][],
) {
const query = table.where(key);
return value instanceof Array ? query.anyOf(value) : query.equals(value);
}
}
// the Dexie sugar
table.mapToClass(Friend);
export default Friend;
db.ts
import Dexie, { Table } from 'dexie';
import { IFriend } from './models/Friend';
export class DataBase extends Dexie {
friends!: Table<IFriend, number>;
constructor() {
super('Projector');
this.version(1).stores({
friends: '++id, &name'
});
}
}
const db = new DataBase();
Main.ts
...
const friend = await Friend.find('name', 'Frank').toArray();
console.log(friend instanceof Friend);
// true <<< good
friend.getAddresses();
// TS: Property 'getAddresses' does not exist on type 'IFriend' <<< the issue
...
由于某种原因,
toArray()
的结果如通过 mapToClass()
所宣传的那样返回,但未正确键入以与 TypeScript 一起使用。有什么办法可以解决这个问题吗?
我尝试编写一个自定义插件,但我认为这没有任何用处。
db.Table.prototype.toArray = Dexie.override(
db.Table.prototype.toArray,
function (original) {
return function () {
return original
.apply(this, arguments)
// this would be dynamic, ideally, but i was just trying to get something to happen
.map((entry) => entry as Friend);
};
},
);
我也可以手动做这样的事情:
const friends = await Friend.find('name', ['Fred', 'Bob']).toArray(f => f as Friend[]);
但这有点违背了目的。
我也尝试过将
Table
类型切换为使用 Friend
来代替
...
export class DataBase extends Dexie {
friends!: Table<Friend, number>; // Class instead of interface
constructor() {
super('Projector');
this.version(1).stores({
friends: '++id, &name'
});
}
}
...
但是在添加新行时只会引发错误,因为由于某种原因,这些方法似乎被枚举到类型中。
预先感谢您的帮助
此问题已在 dexie 4 中得到解决。请参阅 https://dexie.org/roadmap/dexie4.0#distinguish-insert--from-output-types
使用
npm install dexie@next
安装 dexie@4。