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

Is there a way to organize my database connections with object literals in 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, PG_PORT, PG_USER, PG_PASSWORD, PG_DB, APP_MODE } = process.env;

// Connections
const connectionsList = {
    production: knex({
        client: "pg",
            connection: {
                host: PG_HOST,
                port: (PG_PORT as any),
                user: PG_USER,
                password: PG_PASSWORD,
                database: PG_DB
            }
    }),

    development: knex({
        client: "sqlite3",
        connection: {
            filename: "./development/dev.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;

但是这样做会给我这个错误:

The element implicitly has a type 'any' because the expression of type 'string' cannot be used for the index type '{ production: Knex<any, unknown[]>; development: Knex<any, unknown[]>; }'. No index signature with a parameter of type 'string' was found in type '{ production: Knex<any, unknown[]>; development: Knex<any, unknown[]>; }'.

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

问题是编译器只知道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