← назад к разделу

Entity - описание таблицы, а не доказательство того, что типы TypeScript и PostgreSQL совпадают. Деньги, идентификаторы и даты задают явно и проверяют на реальном драйвере.

Колонки явно

import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';

@Entity('products')
export class Product {
  @PrimaryGeneratedColumn()
  id: number;

  @Column({ type: 'varchar', length: 200 })
  name: string;

  @Column({ type: 'numeric', precision: 12, scale: 2 })
  price: number;

  @Column({ type: 'timestamptz' })
  createdAt: Date;
}

precision ограничивает общее число цифр, scale - цифры после запятой. timestamptz хранит момент времени, а не локальную дату без часового пояса.

numeric и bigint приходят строками

PostgreSQL-драйвер возвращает numeric и bigint как строки, чтобы не потерять точность JavaScript-числа. TypeORM не делает их безопасными автоматически.

Для денег можно сохранить копейки в bigint и преобразовывать их в transformer:

const money = {
  to: (kopecks: number): number => kopecks,
  from: (raw: string): number => Number(raw),
};

Если значение не помещается в безопасный диапазон Number, используют BigInt или decimal-библиотеку. Сначала выбирают доменную модель, затем transformer, а не наоборот.

Проверять на PostgreSQL

Тест на реальной базе должен показать тип и значение после чтения. Сравнение price === 4990 ловит строку "4990.00" сразу, а вот арифметическая ошибка округления видна только на границах.

Не используйте float для денег. Для UUID выбирайте uuid, для JSONB - jsonb, но не складывайте туда поля, по которым нужны индексы и ограничения без продуманной модели.

Коротко

  • Тип колонки задают явно и сверяют с доменом.
  • numeric и bigint обычно требуют преобразования из строки.
  • Деньги хранят в копейках или decimal-типе, не во float.
  • timestamptz хранит момент времени.
  • Типы проверяют на PostgreSQL, а не по одному виду entity.

Что почитать дальше