我正在尝试在typescript中实现一个惰性数据库连接集合。我正在创建一个名为DatabaseCollection
的类,我的想法是使用代理来延迟加载连接(我使用knex作为连接器),这个类在节点上下文方面工作得很好,但Typescript说“属性不存在”,我不知道如何告诉Typescript动态属性类。
我使用了一个转换为Record<string, Knex>
的变通方法,但我想正确地实现它,提前感谢。
下面是代码:
const debug = true
process.env['DB_TEST_URL'] = 'mysql://root@localhost/test';
class NotImportantConnector {
public url: string;
constructor(url: string) {
this.url = url;
}
}
function dbFromURL(url: string): NotImportantConnector {
return new NotImportantConnector(url);
}
class DatabasesCollection<T> {
protected databases: Record<string, T> = {};
constructor() {
return new Proxy(this, this) as any;
}
get (target: DatabasesCollection<T>, name: string): T {
if (name in target.databases) {
return target.databases[name];
}
const envName = `DB_${name.replace(/[A-Z]/g, l => `_${l}`).toUpperCase()}_URL`;
if (!process.env[envName]) {
throw new Error(`Call to database ${name} needs ${envName} environment variable to run`);
}
target.databases[name] = dbFromURL(process.env[envName] as string) as T;
return target.databases[name];
}
}
// Not working with error Property 'test' does not exist on type 'DatabasesCollection<NotImportantConnector>'.
// const db = new DatabasesCollection<NotImportantConnector>();
// Workaround, using as
const db = new DatabasesCollection<NotImportantConnector>() as unknown as Record<string, NotImportantConnector>;
console.log(db.test);
1条答案
按热度按时间gz5pxeao1#
TypeScript不会让使用
class
声明来使DatabasesCollection<T>
按照您想要的方式运行变得容易,也就是说,每个示例都应该具有类的所有已知属性,加上索引签名,其中每个其他属性键都具有T
类型的值。但是TypeScript不能直接表示这种“每隔一个属性”的概念;在microsoft/TypeScript#17867上有一个长期的开放特性请求,但它还不是语言的一部分。因此,直接向类添加索引签名不会完全按照预期的方式进行。参见How to define Typescript type as a dictionary of strings but with one numeric "id" property,了解一般的各种替代方法。对于您的用例,可以将
DatabasesCollection<T>
的示例作为已知示例类型与{[k: string]: T}
(又名Record<string, T>
)的交集。这在访问该类型的值时表现得足够好…尽管很难以编译器认为是类型安全的方式实际 * 生成 * 该类型的值。这意味着我们需要使用类似类型Assert的东西来说服编译器,你的类构造函数产生了该类型的示例。
为此,我建议将
DatabasesCollection<T>
类重新命名为_DatabasesCollection<T>
,这样我们就可以使用名称DatabasesCollection
作为所需示例类型的名称(_DatabasesCollection<T> & Record<string, T>
),以及Assert的类构造函数的名称。像这样:让我们来测试一下:
看起来不错
Playground链接到代码