有没有办法用 TypeScript 中的对象文字来组织我的数据库连接?

问题描述

我想在我的代码中编写两种不同的连接:一种用于生产,另一种用于开发。我还想使用 .env 文件在这些连接之间进行选择。我尝试了几种方法,我找到了这个解决方案:

// Modules
import dotenv from "dotenv";
import knex from "knex";

// Environment variables
dotenv.config();
const { PG_HOST,PG_PORT,PG_USER,PG_PASSWORD,PG_DB,IS_APP_IN_PRODUCTION } = process.env;

// Connections
const connectionsList = [
    knex({
        client: "pg",connection: {
                host: PG_HOST,port: (PG_PORT as any),user: PG_USER,password: PG_PASSWORD,database: PG_DB
            }
    }),knex({
        client: "sqlite3",connection: {
            filename: "./development/dev.sqlite3",database: "dev-db"
        }
    })
];

// Setting the connection
const connection = connectionsList[IS_APP_IN_PRODUCTION == "true" ? 0 : 1];

// Export
export default connection;

这有效并解决了我的问题,但在我看来,这不是最佳解决方案。我不喜欢使用数组来组织我的连接的想法;因此,我的第一次尝试是使用对象字面量来组织,如下所示:

// Modules
import dotenv from "dotenv";
import knex from "knex";

// Environment variables
dotenv.config();
const { PG_HOST,APP_MODE } = process.env;

// Connections
const connectionsList = {
    production: knex({
        client: "pg",development: knex({
        client: "sqlite3",database: "dev-db"
        }
    })
};

// Setting the connection
const connection = connectionsList[APP_MODE as string]; // APP_MODE is a string that can be "production" or "development"

// Export
export default connection;

但是这样做给了我这个错误

元素隐式具有类型'any',因为类型'string'的表达式不能用于索引类型'{ production: Knex;发展: Knex; }'。 在类型 '{ production: Knex; 中找不到带有类型为 'string' 的参数的索引签名发展: Knex; }'。

有没有办法解决这个问题?如果是,如何?如果没有,我应该如何编写代码

解决方法

问题在于编译器只知道 APP_MODE 是一个 string(或者实际上是 string | undefined),这不足以让编译器确定它是connectionsList。就所有编译器所知,APP_MODE === "testing",然后您正在查找不存在的 connectionsList.testing

您可以显式测试 APP_MODE,之后编译器会很高兴:

if (APP_MODE !== "production" && APP_MODE !== "development") 
  throw new Error("Uh oh,bad APP_MODE");

const connection = connectionsList[APP_MODE]; // okay

或者,您可以只 assert 表示 APP_MODE 是这两个值之一而不是 string

const connection = connectionsList[APP_MODE as "development" | "production"]; // okay

显式测试比断言更安全(因为前者捕获边缘情况而后者没有),但两种方式都让编译器知道 APP_MODE 可以被视为 connectionsList 的键。

Playground link to code