The TypeScript codegen maps every ClickHouse Decimal* column to number, but /v1/query returns Decimals as quoted strings. A generated interface therefore types a field as number while it holds "12.34" at runtime — arithmetic on it silently produces string concatenation or NaN, with no type error.
Evidence
clients/ts/src/cli/codegen.ts puts "Decimal" in the numeric bucket, emitting number.
- The server does not convert Decimals on the way out:
internal/api/clickhouse_exec.go's transformRow special-cases only uuid.UUID, [16]byte, and time.Time. A Decimal column arrives from clickhouse-go as a shopspring/decimal.Decimal, which marshals to a quoted JSON string (there is no MarshalJSONWithoutQuotes override in the repo).
- The Go SDK documents the actual behavior:
docs/src/content/docs/sdk/go/reference.md maps Decimal* → string ("marshaled as a quoted string on the structured-query path").
So the two SDKs' generated types disagree about the same endpoint, and the TypeScript one is wrong.
Options
- Fix the codegen — emit
string for Decimal* in the TS CLI, matching the Go generator and the wire. Breaking for anyone who has generated types and is (incorrectly) doing arithmetic, but the arithmetic was already broken.
- Fix the server — normalize Decimals to unquoted JSON numbers in
transformRow. Loses precision above float64 for high-scale Decimals, which is usually the reason a schema uses Decimal in the first place. Not recommended.
- Emit a branded type (
type Decimal = string) so the generated field is self-documenting and arithmetic is a type error.
Option 1 or 3. This is the same family as #436 (Array(UInt8) base64), which is already tracked against the server side.
Interim
docs/src/content/docs/sdk/reference.md now footnotes the Decimal* row with the real wire shape and links here.
Found during PR #434 review, where the Go SDK's type table made the discrepancy visible.
The TypeScript codegen maps every ClickHouse
Decimal*column tonumber, but/v1/queryreturns Decimals as quoted strings. A generated interface therefore types a field asnumberwhile it holds"12.34"at runtime — arithmetic on it silently produces string concatenation orNaN, with no type error.Evidence
clients/ts/src/cli/codegen.tsputs"Decimal"in the numeric bucket, emittingnumber.internal/api/clickhouse_exec.go'stransformRowspecial-cases onlyuuid.UUID,[16]byte, andtime.Time. ADecimalcolumn arrives fromclickhouse-goas ashopspring/decimal.Decimal, which marshals to a quoted JSON string (there is noMarshalJSONWithoutQuotesoverride in the repo).docs/src/content/docs/sdk/go/reference.mdmapsDecimal*→string("marshaled as a quoted string on the structured-query path").So the two SDKs' generated types disagree about the same endpoint, and the TypeScript one is wrong.
Options
stringforDecimal*in the TS CLI, matching the Go generator and the wire. Breaking for anyone who has generated types and is (incorrectly) doing arithmetic, but the arithmetic was already broken.transformRow. Loses precision above float64 for high-scale Decimals, which is usually the reason a schema uses Decimal in the first place. Not recommended.type Decimal = string) so the generated field is self-documenting and arithmetic is a type error.Option 1 or 3. This is the same family as #436 (
Array(UInt8)base64), which is already tracked against the server side.Interim
docs/src/content/docs/sdk/reference.mdnow footnotes theDecimal*row with the real wire shape and links here.Found during PR #434 review, where the Go SDK's type table made the discrepancy visible.