跳至主要內容

JS codec 與 Int64

JavaScript 缺乏原生的 Decimal, UUID,以及僅包含日期 (date-only) 或僅包含時間 (time-only) 的值。TTOON 的 codec 系統允許您自訂這些型別在 JS 中的表示方式。

預設行為 (無 codec)

typed typeJS 結果原因
intnumber (溢位則拋出錯誤)預設為安全機制
floatnumber原生支援
decimalstring (移除 m)無原生 Decimal
datestring無僅含日期的原生型別
timestring無僅含時間的原生型別
datetimestring避免 Date 物件的怪異行為
uuidstring無原生 UUID
binaryUint8Array原生支援
null, bool, string原生對應型別原生支援

Int64 策略

JS 的 number 只能安全地表示介於 -(2^53 - 1)2^53 - 1 之間的整數。TTOON 提供了三種可選模式(加上預設的安全錯誤行為):

預設:安全的錯誤

import { parse } from '@ttoon/shared';

parse('9223372036854775807'); // 拋出錯誤: 超出安全整數範圍

不會有靜默的精確度流失。這是最安全的預設值。

BigInt 模式

import { use, intBigInt } from '@ttoon/shared';

await use({ int: intBigInt() });

const value = parse('9223372036854775807');
console.log(value); // 9223372036854775807n (BigInt)

所有的整數都會變成 BigInt,即使是小整數。

NaN 模式

import { use, intNumber } from '@ttoon/shared';

await use({ int: intNumber({ overflow: 'nan' }) });

const value = parse('9223372036854775807');
console.log(value); // NaN

發生溢位時,intNumber() 會回傳 NaN 而非拋出錯誤。

損失模式

import { use, intNumber } from '@ttoon/shared';

await use({ int: intNumber({ overflow: 'lossy' }) });

const value = parse('9223372036854775807');
console.log(value); // 9223372036854776000 (精度遺失)

明確接受精確度的流失。請僅在您確定您的資料適用,或是不在意精確度時使用。

自訂 codec

Decimal codec 範例

import Decimal from 'decimal.js';
import { use, type Codec } from '@ttoon/shared';

const decimalCodec: Codec<Decimal> = {
type: 'decimal',
fromPayload(payload) {
if (typeof payload !== 'string') throw new Error('expected decimal payload');
return new Decimal(payload.slice(0, -1)); // 移除 'm' 後綴
},
toPayload(value) {
return `${value.toString()}m`;
},
is(value): value is Decimal {
return value instanceof Decimal;
},
};

await use({ decimal: decimalCodec });

註冊後:

const data = parse('price: 123.45m');
// data.price 現在是一個 Decimal 實例,而不是字串

Codec 介面

interface Codec<T> {
type?: CodecType; // 'int' | 'decimal' | 'date' | 'time' | 'datetime' | 'uuid' | 'binary'
fromPayload(payload: CodecPayload): T; // payload → JS 值
toPayload(value: T): CodecPayload; // JS 值 → payload
is(value: unknown): value is T; // Type guard
}

註冊多個 codec

await use({
int: intBigInt(),
decimal: decimalCodec,
date: dateCodec,
uuid: uuidCodec,
});

單次呼叫的覆寫

const data = parse(text, {
codecs: { int: intBigInt() }, // 僅在這次呼叫中覆寫全域設定
});

codec 的作用範圍

codec 會在 parse(), stringify(), toTjson() 和物件路徑的串流讀寫器中影響 JS 的物件路徑值對應。它們不會影響:

  • T-TOON / T-JSON 語法 (文字格式總是一樣的)
  • Rust 或 Python 的型別行為
  • Arrow Schema 推論 (readArrow() / stringifyArrow())
  • 轉碼操作 (tjsonToTtoon() / ttoonToTjson())

這是刻意設計的 — codec 是一個特定於 JS 的適配層,而不是格式等級的功能。

帶有 codec 的串流

物件路徑的串流讀寫器同樣會遵循 codec:

import { streamRead, StreamSchema, types, use, intBigInt } from '@ttoon/shared';

await use({ int: intBigInt() });

const schema = new StreamSchema({ id: types.int, name: types.string });

for await (const row of streamRead(source, { schema })) {
console.log(typeof row.id); // 'bigint'
}

Arrow 串流不會受 codec 影響 — Arrow 會使用其原生型別。

建議策略

情境策略
保證是小整數的資料預設 (不使用 codec)
可能會有 int64 值的資料intBigInt()
財務/會計資料自訂搭配 decimal.jsbig.jsdecimal codec
偏重日期的應用程式自訂的 date/time/datetime codec
對效能要求極高的 Arrow 流水線略過 codec,直接使用 Arrow 路徑