Есть ли способ организовать мои соединения с базой данных с помощью объектных литералов в TypeScript?

#typescript #database-connection #javascript-objects #knex.js #dotenv

Вопрос:

Я хочу написать в своем коде два разных соединения: одно предназначено для производства, а другое-для разработки. Я также хочу выбрать между этими соединениями с .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;
 

Но поступая таким образом, я получаю эту ошибку:

Элемент неявно имеет тип «любой», поскольку выражение типа «строка» не может использоваться для типа индекса » {производство: Knex<любой, неизвестный[]>; разработка: Knex<любой, неизвестный[]><любой, неизвестный[]>; }». В типе «{производство: Knex<любой, неизвестный[]>; разработка: Knex<любой, неизвестный[]><любой, неизвестный []>;} «не была найдена подпись индекса с параметром типа «строка».

Есть ли способ решить эту проблему? Если да, то как? А если нет, то как мне написать свой код?

Ответ №1:

Проблема в том, что компилятор знает только, что APP_MODE это string (или на самом деле string | undefined ), чего недостаточно для компилятора, чтобы быть уверенным, что это ключ connectionsList . Насколько известно компилятору, APP_MODE === "testing" , а затем вы ищете connectionsList.testing то, чего не существует.

Вы можете либо явно протестировать APP_MODE , после чего компилятор будет доволен:

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

const connection = connectionsList[APP_MODE]; // okay
 

Или вы можете просто утверждать, что APP_MODE это одно из этих двух значений, а не string :

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

Явное тестирование безопаснее, чем утверждение (поскольку первое улавливает крайние случаи, а второе-нет), но оба способа позволяют компилятору знать, что APP_MODE это можно рассматривать как ключ connectionsList .

Ссылка на игровую площадку для кода

Комментарии:

1. Спасибо за ответ!