@guanriyue/decurl/decode
@guanriyue/decurl/decode 提供一组可选的 decode primitives。它们用于组合常见字符串解析逻辑,但不是必需品;复杂结构可以直接编写自定义 decode。
import { pipe, shape, toNumber, trim } from '@guanriyue/decurl/decode';
失败语义
Decode<TInput, TOutput> 是一个同步函数。返回 null 或 undefined 表示解析失败,pipe 会停止后续 step。
import type { Decode } from '@guanriyue/decurl/decode';
const compact: Decode<string, string> = (value) => {
const nextValue = value.trim();
return nextValue.length > 0 ? nextValue : undefined;
};
Composition
组合工具负责组织 decode pipeline。
mapItems
mapItems(...steps) 对数组中每一项执行 pipe-like decode。解析失败的 item 会被过滤。
import { mapItems, pipe, shape, toNumber, unique } from '@guanriyue/decurl/decode';
const decodeIds = pipe(
mapItems(shape.integer, toNumber),
unique,
);
pipe
pipe(...steps) 按顺序执行多个 decode step。输入类型来自第一个 step,输出类型来自最后一个 step。
遇到 null / undefined 时,pipe 会停止后续 step,并返回 undefined。
import { min, pipe, shape, toNumber, trim } from '@guanriyue/decurl/decode';
const decodePage = pipe(trim, shape.integer, toNumber, min(1));
Normalize
Normalize API 用于先把原始字符串整理成更适合后续解析的形式。
trim
trim(input) 去除字符串两端空白。
import { trim } from '@guanriyue/decurl/decode';
trim(' decurl ');
// 'decurl'
trim.end
trim.end(input) 去除字符串结尾空白。
trim.end(' decurl ');
// ' decurl'
trim.start
trim.start(input) 去除字符串开头空白。
trim.start(' decurl ');
// 'decurl '
Shape / Guard
Shape 和 Guard API 只判断值是否符合要求,不负责转换值。不符合要求时返回 undefined。
elementOf
elementOf(values) 要求值属于指定数组。它使用 Object.is 做成员判断,不做类型转换。
import { elementOf, pipe, shape, toNumber } from '@guanriyue/decurl/decode';
const decodePageSize = pipe(
shape.integer,
toNumber,
elementOf([20, 50, 100]),
);
elementOf(enumDefinition)
elementOf(enumDefinition) 要求值属于 enum definition 的 value。对于数字 enum,通常需要先自行转换为 number。
enum PageSize {
Small = 20,
Large = 50,
}
const decodePageSize = pipe(shape.integer, toNumber, elementOf(PageSize));
shape
shape(regexp) 要求字符串符合指定正则。输出仍然是原始字符串。
import { shape } from '@guanriyue/decurl/decode';
const nonEmpty = shape(/.+/);
shape.boolish
shape.boolish 要求字符串是 'true' 或 'false',并把类型收窄为 'true' | 'false'。
import { pipe, shape, toBoolean } from '@guanriyue/decurl/decode';
const decodeEnabled = pipe(shape.boolish, toBoolean);
shape.date
shape.date 要求字符串是 YYYY-MM-DD,并检查真实日期。
shape.date('2026-02-30');
// undefined
shape.datetime
shape.datetime 要求字符串符合常见日期时间结构,并检查日期和时间范围。
shape.datetime('2026-06-07T13:45:30Z');
// '2026-06-07T13:45:30Z'
shape.integer
shape.integer 要求字符串符合十进制整数形状。它不把字符串转换为 number。
import { pipe, shape, toNumber } from '@guanriyue/decurl/decode';
const decodeInteger = pipe(shape.integer, toNumber);
shape.month
shape.month 要求字符串是 YYYY-MM,并检查月份范围。
shape.month('2026-06');
// '2026-06'
shape.number
shape.number 要求字符串符合十进制数字形状。它不把字符串转换为 number。
const decodeNumber = pipe(shape.number, toNumber);
startsWith
startsWith(prefix) 要求字符串以前缀开头,并把类型收窄为模板字面量类型。
import { startsWith } from '@guanriyue/decurl/decode';
const decodeUserId = startsWith('user_');
where
where(predicate) 使用 predicate 或 type guard 过滤值。predicate 返回 false 时,decode 结果是 undefined。
import { where } from '@guanriyue/decurl/decode';
const positive = where((value: number) => value > 0);
Convert
Convert API 负责把值转换成新的类型。
toBoolean
toBoolean(input) 把 'true' / 'false' 转换为 boolean。其他输入会得到 undefined。
import { shape, pipe, toBoolean } from '@guanriyue/decurl/decode';
const decodeBoolean = pipe(shape.boolish, toBoolean);
toNumber
toNumber(input) 使用 Number(input) 转换,并要求结果是 finite number。
import { shape, pipe, toNumber } from '@guanriyue/decurl/decode';
const decodeNumber = pipe(shape.number, toNumber);
Constraint
Constraint API 用于继续收窄有效范围。它们只做约束判断,不改变输入值。
length
length(size) 要求输入值的 length 等于指定值。
length([min, max]) 要求输入值的 length 位于闭区间。
import { length, pipe, trim } from '@guanriyue/decurl/decode';
const decodeCode = pipe(trim, length(6));
const decodeKeyword = pipe(trim, length([1, 40]));
length.max
length.max(value) 要求输入值的 length 小于等于指定值。
const decodeKeyword = pipe(trim, length.max(40));
length.min
length.min(value) 要求输入值的 length 大于等于指定值。
const decodeKeyword = pipe(trim, length.min(1));
max
max(value) 要求 number 小于等于指定值。不改变输入值;不符合约束时返回 undefined。
import { max, pipe, shape, toNumber } from '@guanriyue/decurl/decode';
const decodePercent = pipe(shape.number, toNumber, max(100));
min
min(value) 要求 number 大于等于指定值。不改变输入值;不符合约束时返回 undefined。
import { min, pipe, shape, toNumber } from '@guanriyue/decurl/decode';
const decodePage = pipe(shape.integer, toNumber, min(1));
Array
Array API 用于数组级处理。
unique
unique(input) 按 Object.is 去重并保留首次出现顺序。
import { mapItems, pipe, shape, toNumber, unique } from '@guanriyue/decurl/decode';
const decodeIds = pipe(
mapItems(shape.integer, toNumber),
unique,
);
unique.by
unique.by(identity) 按 identity 结果去重。
import { unique } from '@guanriyue/decurl/decode';
const uniqueById = unique.by((item: { id: number }) => item.id);