属性与类型
模型里的每个字段都是一个属性声明。快速上手章已见标量,这一节补全所有类型与它们的边界。
标量属性
| 声明 | TS 类型 | 语义 | PostgreSQL |
|---|---|---|---|
prop.i8() / i16() / i32() / i64() | number(i64 可用 asString()) | 有符号整数 | SMALLINT ~ BIGINT |
prop.num(p, s) | number | 定点数(金额等) | NUMERIC(p, s) |
prop.f32() / f64() | number | 浮点 | FLOAT / DOUBLE PRECISION |
prop.str(n) | string | 定长字符串 | VARCHAR(n) |
prop.text() | string | 长文本 | TEXT |
prop.bool() | boolean | 布尔 | BOOLEAN |
prop.dt() | Date | 日期时间 | TIMESTAMP |
i64 的精度问题
BIGINT 可超过 Number.MAX_SAFE_INTEGER(2⁵³)。读回为 number 会丢精度,框架提供两个选择:
id = prop.i64().asString() // 以 string 读写 BIGINT;超过 2⁵³ 的 id 用它
id = prop.i64() // 否则直接用 number 即可
asString() 自动在数据库 BIGINT 与 string 之间转换,查询、保存、主键全部一致。
可空性
默认不可空,NULL 需要显式声明:
store = prop.m2o(BookStore) // 非空:每条 Book 必须有 store
store = prop.m2o(BookStore).nullable() // 可空:允许 NULL(表里允许外键为空)
orchidName = prop.str(50).nullable() // 标量同理
可空性会传导到 DTO 类型:视图里的该字段类型是 T | null,这也让 TS 的 null 检查贯穿查询与保存。
枚举与枚举集合
prop.enum 两种写法:枚举常量表({ 逻辑名: 值 })或值列表(至少两个):
gender = prop.enum({ // 逻辑名与存储值分离
MALE: 'M',
FEMALE: 'F',
})
tag = prop.enum("URGENT", "NORMAL", "LOW") // 直接值列表
features = prop.enumSet("READING_ROOM", "AIR_CONDITION") // 多选:数组
- 枚举值只允许字符串/数字,且值必须唯一;
enumSet在库里落为字符串集合(分隔存储),DTO 类型是数组。
JSON 与 JSONB(带结构校验)
prop.json / prop.jsonb 接受一个 StandardSchemaV1 结构(zod 等库天然兼容),字段的 TS 类型自动取 schema 的输出类型:
import { z } from 'zod'
details = prop.json(z.object({
isbn: z.string(),
rating: z.number().min(1).max(5),
}))
- 结构只描述字段形状与转换,运行时不含反射(「DTO 哲学」讲为什么);
json存JSON、jsonb存JSONB(二进制,可索引)。
自定义数据类型(进阶)
内置类型不够时用 prop.scalar + ScalarProvider 定义自定义数据类型(比如把数据库二进制映射成图片对象),完整机制与示例见 「查询 API → 自定义数据类型」。日常开发很少用到。
下一步
表与字段都齐了,接下来是重头戏:关联——四种关联如何声明、中间表、级联与非对称。