Effect 数据类型
使用 schema 转换并管理各类数据类型,以获得更好的 JSON 序列化支持,涵盖 Option、Either、Set、Map、Duration 以及敏感的 Redacted 数据。
与 Data 互操作
Effect 生态中的 Data 模块会自动实现 Equal 与 Hash 两个 trait,从而简化值比较。这样一来就无需手动实现,相等性检查也变得直接明了。
示例(用 Data 比较 Struct)
import { Data, Equal } from "effect"
const person1 = Data.struct({ name: "Alice", age: 30 })
const person2 = Data.struct({ name: "Alice", age: 30 })
console.log(Equal.equals(person1, person2))
// Output: true
默认情况下,Schema.Struct 这类 schema 并不会实现 Equal 与 Hash 这两个 trait。这意味着两个值完全相同的已解码对象不会被视为相等。
示例(不使用 Equal 与 Hash 时的默认行为)
import { Schema } from "effect"
import { Equal } from "effect"
const schema = Schema.Struct({
name: Schema.String,
age: Schema.Number,
})
const decode = Schema.decode(schema)
const person1 = decode({ name: "Alice", age: 30 })
const person2 = decode({ name: "Alice", age: 30 })
console.log(Equal.equals(person1, person2))
// Output: false
Schema.Data 函数可以用来增强一个 schema,让它带上 Equal 与 Hash 这两个 trait。这样得到的对象就支持基于值的相等性。
示例(使用 Schema.Data 添加相等性)
import { Schema } from "effect"
import { Equal } from "effect"
const schema = Schema.Data(
Schema.Struct({
name: Schema.String,
age: Schema.Number,
}),
)
const decode = Schema.decode(schema)
const person1 = decode({ name: "Alice", age: 30 })
const person2 = decode({ name: "Alice", age: 30 })
console.log(Equal.equals(person1, person2))
// Output: true
Config
Schema.Config 函数允许你使用结构化的 schema 来解码并管理应用的配置项。
它保证配置数据的一致性,并为解码错误提供详细的反馈。
语法
Config: <A, I extends string>(name: string, schema: Schema<A, I>) => Config<A>
该函数接收两个参数:
name:配置项的标识符。schema:描述期望的数据类型与结构的 schema。
它返回一个 Config 对象,可与你的应用配置系统集成。
Encoded 类型 I 必须扩展自 string,因此该 schema 必须能够从字符串解码,这包括 Schema.String、Schema.Literal("...") 或 Schema.NumberFromString 这类 schema,并且它们还可以附加 refinement。
在幕后,Schema.Config 会执行以下步骤:
- 获取值:使用提供的名称(例如从环境变量中获取)。
- 解码值:使用给定的 schema。如果值无效,解码就会失败。
- 格式化错误:使用 TreeFormatter.formatErrorSync,它有助于产出可读且详细的错误消息。
示例(解码一个配置值)
import { Effect, Schema } from "effect"
// Define a config that expects a string with at least 4 characters
const myConfig = Schema.Config("Foo", Schema.String.pipe(Schema.minLength(4)))
const program = Effect.gen(function* () {
const foo = yield* myConfig
console.log(`ok: ${foo}`)
})
Effect.runSync(program)
要测试这份配置,请执行以下命令:
测试(配置数据缺失)
npx tsx config.ts
# Output:
# [(Missing data at Foo: "Expected Foo to exist in the process context")]
测试(数据无效)
Foo=bar npx tsx config.ts
# Output:
# [(Invalid data at Foo: "a string at least 4 character(s) long
# └─ Predicate refinement failure
# └─ Expected a string at least 4 character(s) long, actual "bar"")]
测试(数据有效)
Foo=foobar npx tsx config.ts
# Output:
# ok: foobar
Option
Option
Schema.Option 函数可用于把 Option 转换成可 JSON 序列化的格式。
语法
Schema.Option(schema: Schema<A, I, R>)
Decoding
| Input | Output |
|---|---|
{ _tag: "None" } | 转换为 Option.none() |
{ _tag: "Some", value: I } | 转换为 Option.some(a):其中 I 用内部 schema 解码为 A |
Encoding
| Input | Output |
|---|---|
Option.none() | 转换为 { _tag: "None" } |
Option.some(A) | 转换为 { _tag: "Some", value: I }:其中 A 用内部 schema 编码为 I |
示例
import { Schema } from "effect"
import { Option } from "effect"
const schema = Schema.Option(Schema.NumberFromString)
// ┌─── OptionEncoded<string>
// ▼
type Encoded = typeof schema.Encoded
// ┌─── Option<number>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(decode({ _tag: "None" }))
// Output: { _id: 'Option', _tag: 'None' }
console.log(decode({ _tag: "Some", value: "1" }))
// Output: { _id: 'Option', _tag: 'Some', value: 1 }
// Encoding examples
console.log(encode(Option.none()))
// Output: { _tag: 'None' }
console.log(encode(Option.some(1)))
// Output: { _tag: 'Some', value: '1' }
OptionFromSelf
Schema.OptionFromSelf 函数面向这样的场景:Option 值已经处于 Option 格式,需要在其内部值按照所提供的 schema 进行转换的同时完成解码或编码。
语法
Schema.OptionFromSelf(schema: Schema<A, I, R>)
Decoding
| Input | Output |
|---|---|
Option.none() | 保持为 Option.none() |
Option.some(I) | 转换为 Option.some(A):其中 I 用内部 schema 解码为 A |
Encoding
| Input | Output |
|---|---|
Option.none() | 保持为 Option.none() |
Option.some(A) | 转换为 Option.some(I):其中 A 用内部 schema 编码为 I |
示例
import { Schema } from "effect"
import { Option } from "effect"
const schema = Schema.OptionFromSelf(Schema.NumberFromString)
// ┌─── Option<string>
// ▼
type Encoded = typeof schema.Encoded
// ┌─── Option<number>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(decode(Option.none()))
// Output: { _id: 'Option', _tag: 'None' }
console.log(decode(Option.some("1")))
// Output: { _id: 'Option', _tag: 'Some', value: 1 }
// Encoding examples
console.log(encode(Option.none()))
// Output: { _id: 'Option', _tag: 'None' }
console.log(encode(Option.some(1)))
// Output: { _id: 'Option', _tag: 'Some', value: '1' }
OptionFromUndefinedOr
Schema.OptionFromUndefinedOr 函数处理这样的场景:undefined 被视为 Option.none(),而根据所提供的 schema,其他所有值都被解释为 Option.some()。
语法
Schema.OptionFromUndefinedOr(schema: Schema<A, I, R>)
Decoding
| Input | Output |
|---|---|
undefined | 转换为 Option.none() |
I | 转换为 Option.some(A):其中 I 用内部 schema 解码为 A |
Encoding
| Input | Output |
|---|---|
Option.none() | 转换为 undefined |
Option.some(A) | 转换为 I:其中 A 用内部 schema 编码为 I |
示例
import { Schema } from "effect"
import { Option } from "effect"
const schema = Schema.OptionFromUndefinedOr(Schema.NumberFromString)
// ┌─── string | undefined
// ▼
type Encoded = typeof schema.Encoded
// ┌─── Option<number>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(decode(undefined))
// Output: { _id: 'Option', _tag: 'None' }
console.log(decode("1"))
// Output: { _id: 'Option', _tag: 'Some', value: 1 }
// Encoding examples
console.log(encode(Option.none()))
// Output: undefined
console.log(encode(Option.some(1)))
// Output: "1"
OptionFromNullOr
Schema.OptionFromUndefinedOr 函数处理这样的场景:null 被视为 Option.none(),而根据所提供的 schema,其他所有值都被解释为 Option.some()。
语法
Schema.OptionFromNullOr(schema: Schema<A, I, R>)
Decoding
| Input | Output |
|---|---|
null | 转换为 Option.none() |
I | 转换为 Option.some(A):其中 I 用内部 schema 解码为 A |
Encoding
| Input | Output |
|---|---|
Option.none() | 转换为 null |
Option.some(A) | 转换为 I:其中 A 用内部 schema 编码为 I |
示例
import { Schema } from "effect"
import { Option } from "effect"
const schema = Schema.OptionFromNullOr(Schema.NumberFromString)
// ┌─── string | null
// ▼
type Encoded = typeof schema.Encoded
// ┌─── Option<number>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(decode(null))
// Output: { _id: 'Option', _tag: 'None' }
console.log(decode("1"))
// Output: { _id: 'Option', _tag: 'Some', value: 1 }
// Encoding examples
console.log(encode(Option.none()))
// Output: null
console.log(encode(Option.some(1)))
// Output: "1"
OptionFromNullishOr
Schema.OptionFromNullishOr 函数处理这样的场景:null 或 undefined 被视为 Option.none(),而根据所提供的 schema,其他所有值都被解释为 Option.some()。此外,它还允许自定义 Option.none() 的编码方式(null 或 undefined)。
语法
Schema.OptionFromNullishOr(
schema: Schema<A, I, R>,
onNoneEncoding: null | undefined
)
Decoding
| Input | Output |
|---|---|
undefined | 转换为 Option.none() |
null | 转换为 Option.none() |
I | 转换为 Option.some(A):其中 I 用内部 schema 解码为 A |
Encoding
| Input | Output |
|---|---|
Option.none() | 根据用户的选择(onNoneEncoding)转换为 undefined 或 null |
Option.some(A) | 转换为 I:其中 A 用内部 schema 编码为 I |
示例
import { Schema } from "effect"
import { Option } from "effect"
const schema = Schema.OptionFromNullishOr(
Schema.NumberFromString,
undefined, // Encode Option.none() as undefined
)
// ┌─── string | null | undefined
// ▼
type Encoded = typeof schema.Encoded
// ┌─── Option<number>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(decode(null))
// Output: { _id: 'Option', _tag: 'None' }
console.log(decode(undefined))
// Output: { _id: 'Option', _tag: 'None' }
console.log(decode("1"))
// Output: { _id: 'Option', _tag: 'Some', value: 1 }
// Encoding examples
console.log(encode(Option.none()))
// Output: undefined
console.log(encode(Option.some(1)))
// Output: "1"
OptionFromNonEmptyTrimmedString
Schema.OptionFromNonEmptyTrimmedString schema 用于处理这样的字符串:去掉首尾空白后为空字符串的,会被当作 Option.none();所有其他字符串则会被转换成 Option.some()。
解码
| Input | Output |
|---|---|
s: string | 如果 s.trim().length > 0,则转换为 Option.some(s) |
否则转换为 Option.none() |
编码
| Input | Output |
|---|---|
Option.none() | 转换为 "" |
Option.some(s: string) | 转换为 s |
示例
import { Schema, Option } from "effect"
// ┌─── string
// ▼
type Encoded = typeof Schema.OptionFromNonEmptyTrimmedString
// ┌─── Option<string>
// ▼
type Type = typeof Schema.OptionFromNonEmptyTrimmedString
const decode = Schema.decodeUnknownSync(Schema.OptionFromNonEmptyTrimmedString)
const encode = Schema.encodeSync(Schema.OptionFromNonEmptyTrimmedString)
// Decoding examples
console.log(decode(""))
// Output: { _id: 'Option', _tag: 'None' }
console.log(decode(" a "))
// Output: { _id: 'Option', _tag: 'Some', value: 'a' }
console.log(decode("a"))
// Output: { _id: 'Option', _tag: 'Some', value: 'a' }
// Encoding examples
console.log(encode(Option.none()))
// Output: ""
console.log(encode(Option.some("example")))
// Output: "example"
Either
Either
Schema.Either 函数可用于把 Either 转换成可 JSON 序列化的格式。
语法
Schema.Either(options: {
left: Schema<LA, LI, LR>,
right: Schema<RA, RI, RR>
})
解码
| Input | Output |
|---|---|
{ _tag: "Left", left: LI } | 转换为 Either.left(LA):其中 LI 用内部 left schema 解码为 LA |
{ _tag: "Right", right: RI } | 转换为 Either.right(RA):其中 RI 用内部 right schema 解码为 RA |
编码
| Input | Output |
|---|---|
Either.left(LA) | 转换为 { _tag: "Left", left: LI }:其中 LA 用内部 left schema 编码为 LI |
Either.right(RA) | 转换为 { _tag: "Right", right: RI }:其中 RA 用内部 right schema 编码为 RI |
示例
import { Schema, Either } from "effect"
const schema = Schema.Either({
left: Schema.Trim,
right: Schema.NumberFromString,
})
// ┌─── EitherEncoded<string, string>
// ▼
type Encoded = typeof schema.Encoded
// ┌─── Either<number, string>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(decode({ _tag: "Left", left: " a " }))
// Output: { _id: 'Either', _tag: 'Left', left: 'a' }
console.log(decode({ _tag: "Right", right: "1" }))
// Output: { _id: 'Either', _tag: 'Right', right: 1 }
// Encoding examples
console.log(encode(Either.left("a")))
// Output: { _tag: 'Left', left: 'a' }
console.log(encode(Either.right(1)))
// Output: { _tag: 'Right', right: '1' }
EitherFromSelf
Schema.EitherFromSelf 函数面向这样的场景:Either 值已经处于 Either 格式,需要在内部值按照所提供的 schema 进行转换的同时完成解码或编码。
语法
Schema.EitherFromSelf(options: {
left: Schema<LA, LI, LR>,
right: Schema<RA, RI, RR>
})
解码
| Input | Output |
|---|---|
Either.left(LI) | 转换为 Either.left(LA):其中 LI 用内部 left schema 解码为 LA |
Either.right(RI) | 转换为 Either.right(RA):其中 RI 用内部 right schema 解码为 RA |
编码
| Input | Output |
|---|---|
Either.left(LA) | 转换为 Either.left(LI):其中 LA 用内部 left schema 编码为 LI |
Either.right(RA) | 转换为 Either.right(RI):其中 RA 用内部 right schema 编码为 RI |
示例
import { Schema, Either } from "effect"
const schema = Schema.EitherFromSelf({
left: Schema.Trim,
right: Schema.NumberFromString,
})
// ┌─── Either<string, string>
// ▼
type Encoded = typeof schema.Encoded
// ┌─── Either<number, string>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(decode(Either.left(" a ")))
// Output: { _id: 'Either', _tag: 'Left', left: 'a' }
console.log(decode(Either.right("1")))
// Output: { _id: 'Either', _tag: 'Right', right: 1 }
// Encoding examples
console.log(encode(Either.left("a")))
// Output: { _id: 'Either', _tag: 'Left', left: 'a' }
console.log(encode(Either.right(1)))
// Output: { _id: 'Either', _tag: 'Right', right: '1' }
EitherFromUnion
Schema.EitherFromUnion 函数用于解码和编码这样的 Either 值:其 left 与 right 两侧被表示为彼此不同的类型。这个 schema 支持在原始联合类型与结构化的 Either 类型之间进行转换。
语法
Schema.EitherFromUnion(options: {
left: Schema<LA, LI, LR>,
right: Schema<RA, RI, RR>
})
解码
| Input | Output |
|---|---|
LI | 转换为 Either.left(LA):其中 LI 用内部 left schema 解码为 LA |
RI | 转换为 Either.right(RA):其中 RI 用内部 right schema 解码为 RA |
编码
| Input | Output |
|---|---|
Either.left(LA) | 转换为 LI:其中 LA 用内部 left schema 编码为 LI |
Either.right(RA) | 转换为 RI:其中 RA 用内部 right schema 编码为 RI |
示例
import { Schema, Either } from "effect"
const schema = Schema.EitherFromUnion({
left: Schema.Boolean,
right: Schema.NumberFromString,
})
// ┌─── string | boolean
// ▼
type Encoded = typeof schema.Encoded
// ┌─── Either<number, boolean>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(decode(true))
// Output: { _id: 'Either', _tag: 'Left', left: true }
console.log(decode("1"))
// Output: { _id: 'Either', _tag: 'Right', right: 1 }
// Encoding examples
console.log(encode(Either.left(true)))
// Output: true
console.log(encode(Either.right(1)))
// Output: "1"
Exit
Exit
Schema.Exit 函数可用于把 Exit 转换成可 JSON 序列化的格式。
语法
Schema.Exit(options: {
failure: Schema<FA, FI, FR>,
success: Schema<SA, SI, SR>,
defect: Schema<DA, DI, DR>
})
解码
| Input | Output |
|---|---|
{ _tag: "Failure", cause: CauseEncoded<FI, DI> } | 转换为 Exit.failCause(Cause<FA>):其中 CauseEncoded<FI, DI> 用内部 failure 与 defect schema 解码为 Cause<FA> |
{ _tag: "Success", value: SI } | 转换为 Exit.succeed(SA):其中 SI 用内部 success schema 解码为 SA |
编码
| Input | Output |
|---|---|
Exit.failCause(Cause<FA>) | 转换为 { _tag: "Failure", cause: CauseEncoded<FI, DI> }:其中 Cause<FA> 用内部 failure 与 defect schema 编码为 CauseEncoded<FI, DI> |
Exit.succeed(SA) | 转换为 { _tag: "Success", value: SI }:其中 SA 用内部 success schema 编码为 SI |
示例
import { Schema, Exit } from "effect"
const schema = Schema.Exit({
failure: Schema.String,
success: Schema.NumberFromString,
defect: Schema.String,
})
// ┌─── ExitEncoded<string, string, string>
// ▼
type Encoded = typeof schema.Encoded
// ┌─── Exit<number, string>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(decode({ _tag: "Failure", cause: { _tag: "Fail", error: "a" } }))
/*
Output:
{
_id: 'Exit',
_tag: 'Failure',
cause: { _id: 'Cause', _tag: 'Fail', failure: 'a' }
}
*/
console.log(decode({ _tag: "Success", value: "1" }))
/*
Output:
{ _id: 'Exit', _tag: 'Success', value: 1 }
*/
// Encoding examples
console.log(encode(Exit.fail("a")))
/*
Output:
{ _tag: 'Failure', cause: { _tag: 'Fail', error: 'a' } }
*/
console.log(encode(Exit.succeed(1)))
/*
Output:
{ _tag: 'Success', value: '1' }
*/
处理序列化中的 defect
Effect 内置了 Defect schema,用于处理 JavaScript 错误(Error 实例)以及其他类型的不可恢复的 defect。
- 解码时,如果输入包含
message,并且可选地包含name和stack,它就会重建出Error实例。 - 编码时,它会把
Error实例转换成仅保留必要属性的普通对象。
当需要在网络请求或日志系统中传递错误,而 Error 对象默认不会被序列化时,这一点非常有用。
示例(编码与解码 defect)
import { Schema, Exit } from "effect"
const schema = Schema.Exit({
failure: Schema.String,
success: Schema.NumberFromString,
defect: Schema.Defect,
})
const decode = Schema.decodeSync(schema)
const encode = Schema.encodeSync(schema)
console.log(encode(Exit.die(new Error("Message"))))
/*
Output:
{
_tag: 'Failure',
cause: { _tag: 'Die', defect: { name: 'Error', message: 'Message' } }
}
*/
console.log(encode(Exit.fail("a")))
console.log(
decode({
_tag: "Failure",
cause: { _tag: "Die", defect: { name: "Error", message: "Message" } },
}),
)
/*
Output:
{
_id: 'Exit',
_tag: 'Failure',
cause: {
_id: 'Cause',
_tag: 'Die',
defect: [Error: Message] { [cause]: [Object] }
}
}
*/
ExitFromSelf
Schema.ExitFromSelf 函数面向这样的场景:Exit 值已经处于 Exit 格式,需要在内部值按照所提供的 schema 进行转换的同时完成解码或编码。
语法
Schema.ExitFromSelf(options: {
failure: Schema<FA, FI, FR>,
success: Schema<SA, SI, SR>,
defect: Schema<DA, DI, DR>
})
解码
| Input | Output |
|---|---|
Exit.failCause(Cause<FI>) | 转换为 Exit.failCause(Cause<FA>):其中 Cause<FI> 用内部 failure 与 defect schema 解码为 Cause<FA> |
Exit.succeed(SI) | 转换为 Exit.succeed(SA):其中 SI 用内部 success schema 解码为 SA |
编码
| Input | Output |
|---|---|
Exit.failCause(Cause<FA>) | 转换为 Exit.failCause(Cause<FI>):其中 Cause<FA> 用内部 failure 与 defect schema 解码为 Cause<FI> |
Exit.succeed(SA) | 转换为 Exit.succeed(SI):其中 SA 用内部 success schema 编码为 SI |
示例
import { Schema, Exit } from "effect"
const schema = Schema.ExitFromSelf({
failure: Schema.String,
success: Schema.NumberFromString,
defect: Schema.String,
})
// ┌─── Exit<string, string>
// ▼
type Encoded = typeof schema.Encoded
// ┌─── Exit<number, string>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(decode(Exit.fail("a")))
/*
Output:
{
_id: 'Exit',
_tag: 'Failure',
cause: { _id: 'Cause', _tag: 'Fail', failure: 'a' }
}
*/
console.log(decode(Exit.succeed("1")))
/*
Output:
{ _id: 'Exit', _tag: 'Success', value: 1 }
*/
// Encoding examples
console.log(encode(Exit.fail("a")))
/*
Output:
{
_id: 'Exit',
_tag: 'Failure',
cause: { _id: 'Cause', _tag: 'Fail', failure: 'a' }
}
*/
console.log(encode(Exit.succeed(1)))
/*
Output:
{ _id: 'Exit', _tag: 'Success', value: '1' }
*/
ReadonlySet
ReadonlySet
Schema.ReadonlySet 函数可用于把 ReadonlySet 转换成可 JSON 序列化的格式。
语法
Schema.ReadonlySet(schema: Schema<A, I, R>)
解码
| Input | Output |
|---|---|
ReadonlyArray<I> | 转换为 ReadonlySet<A>:其中 I 用内部 schema 解码为 A |
编码
| Input | Output |
|---|---|
ReadonlySet<A> | ReadonlyArray<I>,其中 A 用内部 schema 编码为 I |
示例
import { Schema } from "effect"
const schema = Schema.ReadonlySet(Schema.NumberFromString)
// ┌─── readonly string[]
// ▼
type Encoded = typeof schema.Encoded
// ┌─── ReadonlySet<number>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(decode(["1", "2", "3"]))
// Output: Set(3) { 1, 2, 3 }
// Encoding examples
console.log(encode(new Set([1, 2, 3])))
// Output: [ '1', '2', '3' ]
ReadonlySetFromSelf
Schema.ReadonlySetFromSelf 函数面向这样的场景:ReadonlySet 值已经处于 ReadonlySet 格式,需要在内部值按照所提供的 schema 进行转换的同时完成解码或编码。
语法
Schema.ReadonlySetFromSelf(schema: Schema<A, I, R>)
解码
| Input | Output |
|---|---|
ReadonlySet<I> | 转换为 ReadonlySet<A>:其中 I 用内部 schema 解码为 A |
编码
| Input | Output |
|---|---|
ReadonlySet<A> | ReadonlySet<I>,其中 A 用内部 schema 编码为 I |
示例
import { Schema } from "effect"
const schema = Schema.ReadonlySetFromSelf(Schema.NumberFromString)
// ┌─── ReadonlySet<string>
// ▼
type Encoded = typeof schema.Encoded
// ┌─── ReadonlySet<number>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(decode(new Set(["1", "2", "3"])))
// Output: Set(3) { 1, 2, 3 }
// Encoding examples
console.log(encode(new Set([1, 2, 3])))
// Output: Set(3) { '1', '2', '3' }
ReadonlyMap
Schema.ReadonlyMap 函数可用于把 ReadonlyMap 转换成可 JSON 序列化的格式。
ReadonlyMap
语法
Schema.ReadonlyMap(options: {
key: Schema<KA, KI, KR>,
value: Schema<VA, VI, VR>
})
解码
| Input | Output |
|---|---|
ReadonlyArray<readonly [KI, VI]> | 转换为 ReadonlyMap<KA, VA>:其中 KI 用内部 key schema 解码为 KA,VI 用内部 value schema 解码为 VA |
编码
| Input | Output |
|---|---|
ReadonlyMap<KA, VA> | 转换为 ReadonlyArray<readonly [KI, VI]>:其中 KA 用内部 key schema 解码为 KI,VA 用内部 value schema 解码为 VI |
示例
import { Schema } from "effect"
const schema = Schema.ReadonlyMap({
key: Schema.String,
value: Schema.NumberFromString,
})
// ┌─── readonly (readonly [string, string])[]
// ▼
type Encoded = typeof schema.Encoded
// ┌─── ReadonlyMap<string, number>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(
decode([
["a", "2"],
["b", "2"],
["c", "3"],
]),
)
// Output: Map(3) { 'a' => 2, 'b' => 2, 'c' => 3 }
// Encoding examples
console.log(
encode(
new Map([
["a", 1],
["b", 2],
["c", 3],
]),
),
)
// Output: [ [ 'a', '1' ], [ 'b', '2' ], [ 'c', '3' ] ]
ReadonlyMapFromSelf
Schema.ReadonlyMapFromSelf 函数面向这样的场景:ReadonlyMap 值已经处于 ReadonlyMap 格式,需要在内部值按照所提供的 schema 进行转换的同时完成解码或编码。
语法
Schema.ReadonlyMapFromSelf(options: {
key: Schema<KA, KI, KR>,
value: Schema<VA, VI, VR>
})
解码
| Input | Output |
|---|---|
ReadonlyMap<KI, VI> | 转换为 ReadonlyMap<KA, VA>:其中 KI 用内部 key schema 解码为 KA,VI 用内部 value schema 解码为 VA |
编码
| Input | Output |
|---|---|
ReadonlyMap<KA, VA> | 转换为 ReadonlyMap<KI, VI>:其中 KA 用内部 key schema 解码为 KI,VA 用内部 value schema 解码为 VI |
示例
import { Schema } from "effect"
const schema = Schema.ReadonlyMapFromSelf({
key: Schema.String,
value: Schema.NumberFromString,
})
// ┌─── ReadonlyMap<string, string>
// ▼
type Encoded = typeof schema.Encoded
// ┌─── ReadonlyMap<string, number>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(
decode(
new Map([
["a", "2"],
["b", "2"],
["c", "3"],
]),
),
)
// Output: Map(3) { 'a' => 2, 'b' => 2, 'c' => 3 }
// Encoding examples
console.log(
encode(
new Map([
["a", 1],
["b", 2],
["c", 3],
]),
),
)
// Output: Map(3) { 'a' => '1', 'b' => '2', 'c' => '3' }
ReadonlyMapFromRecord
Schema.ReadonlyMapFromRecord 函数是一个工具,用于把 ReadonlyMap 转换成对象格式(键为字符串、值为可序列化),反之亦然。
语法
Schema.ReadonlyMapFromRecord({
key: Schema<KA, KI, KR>,
value: Schema<VA, VI, VR>,
})
解码
| Input | Output |
|---|---|
{ readonly [x: string]: VI } | 转换为 ReadonlyMap<KA, VA>:其中 x 用 key schema 解码为 KA,VI 用 value schema 解码为 VA |
编码
| Input | Output |
|---|---|
ReadonlyMap<KA, VA> | 转换为 { readonly [x: string]: VI }:其中 KA 用 key schema 编码为 x,VA 用 value schema 编码为 VI |
示例
import { Schema } from "effect"
const schema = Schema.ReadonlyMapFromRecord({
key: Schema.NumberFromString,
value: Schema.NumberFromString,
})
// ┌─── { readonly [x: string]: string; }
// ▼
type Encoded = typeof schema.Encoded
// ┌─── ReadonlyMap<number, number>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(
decode({
"1": "4",
"2": "5",
"3": "6",
}),
)
// Output: Map(3) { 1 => 4, 2 => 5, 3 => 6 }
// Encoding examples
console.log(
encode(
new Map([
[1, 4],
[2, 5],
[3, 6],
]),
),
)
// Output: { '1': '4', '2': '5', '3': '6' }
HashSet
HashSet
Schema.HashSet 函数提供了一种在 HashSet 与数组表示之间互相映射的方式,从而支持 JSON 序列化与反序列化。
语法
Schema.HashSet(schema: Schema<A, I, R>)
解码
| Input | Output |
|---|---|
ReadonlyArray<I> | 转换为 HashSet<A>:使用该 schema 把数组中的每个元素解码为类型 A |
编码
| Input | Output |
|---|---|
HashSet<A> | 转换为 ReadonlyArray<I>:使用该 schema 把 HashSet 中的每个元素编码为类型 I |
示例
import { Schema } from "effect"
import { HashSet } from "effect"
const schema = Schema.HashSet(Schema.NumberFromString)
// ┌─── readonly string[]
// ▼
type Encoded = typeof schema.Encoded
// ┌─── HashSet<number>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(decode(["1", "2", "3"]))
// Output: { _id: 'HashSet', values: [ 1, 2, 3 ] }
// Encoding examples
console.log(encode(HashSet.fromIterable([1, 2, 3])))
// Output: [ '1', '2', '3' ]
HashSetFromSelf
Schema.HashSetFromSelf 函数面向这样的场景:HashSet 值已经处于 HashSet 格式,需要在内部值按照所提供的 schema 进行转换的同时完成解码或编码。
语法
Schema.HashSetFromSelf(schema: Schema<A, I, R>)
解码
| Input | Output |
|---|---|
HashSet<I> | 转换为 HashSet<A>:使用该 schema 把每个元素从类型 I 解码为类型 A |
编码
| Input | Output |
|---|---|
HashSet<A> | 转换为 HashSet<I>:使用该 schema 把每个元素从类型 A 编码为类型 I |
示例
import { Schema } from "effect"
import { HashSet } from "effect"
const schema = Schema.HashSetFromSelf(Schema.NumberFromString)
// ┌─── HashSet<string>
// ▼
type Encoded = typeof schema.Encoded
// ┌─── HashSet<number>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(decode(HashSet.fromIterable(["1", "2", "3"])))
// Output: { _id: 'HashSet', values: [ 1, 2, 3 ] }
// Encoding examples
console.log(encode(HashSet.fromIterable([1, 2, 3])))
// Output: { _id: 'HashSet', values: [ '1', '3', '2' ] }
HashMap
HashMap
Schema.HashMap 函数可用于把 HashMap 转换成可 JSON 序列化的格式。
语法
Schema.HashMap(options: {
key: Schema<KA, KI, KR>,
value: Schema<VA, VI, VR>
})
| Input | Output |
|---|---|
ReadonlyArray<readonly [KI, VI]> | 转换为 HashMap<KA, VA>:其中 KI 用指定的 schema 解码为 KA,VI 用指定的 schema 解码为 VA |
编码
| Input | Output |
|---|---|
HashMap<KA, VA> | 转换为 ReadonlyArray<readonly [KI, VI]>:其中 KA 用指定的 schema 编码为 KI,VA 用指定的 schema 编码为 VI |
示例
import { Schema } from "effect"
import { HashMap } from "effect"
const schema = Schema.HashMap({
key: Schema.String,
value: Schema.NumberFromString,
})
// ┌─── readonly (readonly [string, string])[]
// ▼
type Encoded = typeof schema.Encoded
// ┌─── HashMap<string, number>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(
decode([
["a", "2"],
["b", "2"],
["c", "3"],
]),
)
// Output: { _id: 'HashMap', values: [ [ 'a', 2 ], [ 'c', 3 ], [ 'b', 2 ] ] }
// Encoding examples
console.log(
encode(
HashMap.fromIterable([
["a", 1],
["b", 2],
["c", 3],
]),
),
)
// Output: [ [ 'a', '1' ], [ 'c', '3' ], [ 'b', '2' ] ]
HashMapFromSelf
Schema.HashMapFromSelf 函数面向这样的场景:HashMap 值已经处于 HashMap 格式,需要在其内部值按照所提供的 schema 进行转换的同时完成解码或编码。
语法
Schema.HashMapFromSelf(options: {
key: Schema<KA, KI, KR>,
value: Schema<VA, VI, VR>
})
解码
| Input | Output |
|---|---|
HashMap<KI, VI> | 转换为 HashMap<KA, VA>:其中 KI 用指定的 schema 解码为 KA,VI 用指定的 schema 解码为 VA |
编码
| Input | Output |
|---|---|
HashMap<KA, VA> | 转换为 HashMap<KI, VI>:其中 KA 用指定的 schema 编码为 KI,VA 用指定的 schema 编码为 VI |
示例
import { Schema } from "effect"
import { HashMap } from "effect"
const schema = Schema.HashMapFromSelf({
key: Schema.String,
value: Schema.NumberFromString,
})
// ┌─── HashMap<string, string>
// ▼
type Encoded = typeof schema.Encoded
// ┌─── HashMap<string, number>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(
decode(
HashMap.fromIterable([
["a", "2"],
["b", "2"],
["c", "3"],
]),
),
)
// Output: { _id: 'HashMap', values: [ [ 'a', 2 ], [ 'c', 3 ], [ 'b', 2 ] ] }
// Encoding examples
console.log(
encode(
HashMap.fromIterable([
["a", 1],
["b", 2],
["c", 3],
]),
),
)
// Output: { _id: 'HashMap', values: [ [ 'a', '1' ], [ 'c', '3' ], [ 'b', '2' ] ] }
SortedSet
SortedSet
Schema.SortedSet 函数提供了在 SortedSet 与数组表示之间相互映射的方式,从而支持 JSON 序列化与反序列化。
语法
Schema.SortedSet(schema: Schema<A, I, R>, order: Order<A>)
解码
| Input | Output |
|---|---|
ReadonlyArray<I> | 转换为 SortedSet<A>:使用该 schema 把数组中的每个元素解码为类型 A |
编码
| Input | Output |
|---|---|
SortedSet<A> | 转换为 ReadonlyArray<I>:使用该 schema 把 SortedSet 中的每个元素编码为类型 I |
示例
import { Schema } from "effect"
import { Number, SortedSet } from "effect"
const schema = Schema.SortedSet(Schema.NumberFromString, Number.Order)
// ┌─── readonly string[]
// ▼
type Encoded = typeof schema.Encoded
// ┌─── SortedSet<number>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(decode(["1", "2", "3"]))
// Output: { _id: 'SortedSet', values: [ 1, 2, 3 ] }
// Encoding examples
console.log(encode(SortedSet.fromIterable(Number.Order)([1, 2, 3])))
// Output: [ '1', '2', '3' ]
SortedSetFromSelf
Schema.SortedSetFromSelf 函数面向这样的场景:SortedSet 值已经处于 SortedSet 格式,需要在其内部值按照所提供的 schema 进行转换的同时完成解码或编码。
语法
Schema.SortedSetFromSelf(
schema: Schema<A, I, R>,
decodeOrder: Order<A>,
encodeOrder: Order<I>
)
解码
| Input | Output |
|---|---|
SortedSet<I> | 转换为 SortedSet<A>:使用该 schema 把每个元素从类型 I 解码为类型 A |
编码
| Input | Output |
|---|---|
SortedSet<A> | 转换为 SortedSet<I>:使用该 schema 把每个元素从类型 A 编码为类型 I |
示例
import { Schema } from "effect"
import { Number, SortedSet, String } from "effect"
const schema = Schema.SortedSetFromSelf(
Schema.NumberFromString,
Number.Order,
String.Order,
)
// ┌─── SortedSet<string>
// ▼
type Encoded = typeof schema.Encoded
// ┌─── SortedSet<number>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
const encode = Schema.encodeSync(schema)
// Decoding examples
console.log(decode(SortedSet.fromIterable(String.Order)(["1", "2", "3"])))
// Output: { _id: 'SortedSet', values: [ 1, 2, 3 ] }
// Encoding examples
console.log(encode(SortedSet.fromIterable(Number.Order)([1, 2, 3])))
// Output: { _id: 'SortedSet', values: [ '1', '2', '3' ] }
Duration
Duration schema 家族支持对各种格式的时长值进行转换与校验,包括 hrtime、毫秒与纳秒。
Duration
把 hrtime(即 [seconds: number, nanos: number])转换为 Duration。
示例
import { Schema } from "effect"
const schema = Schema.Duration
// ┌─── readonly [seconds: number, nanos: number]
// ▼
type Encoded = typeof schema.Encoded
// ┌─── Duration
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
// Decoding examples
console.log(decode([0, 0]))
// Output: { _id: 'Duration', _tag: 'Millis', millis: 0 }
console.log(decode([5000, 0]))
// Output: { _id: 'Duration', _tag: 'Nanos', hrtime: [ 5000, 0 ] }
DurationFromSelf
DurationFromSelf schema 用于校验给定值是否符合 Duration 类型。
示例
import { Schema, Duration } from "effect"
const schema = Schema.DurationFromSelf
// ┌─── Duration
// ▼
type Encoded = typeof schema.Encoded
// ┌─── Duration
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
// Decoding examples
console.log(decode(Duration.seconds(2)))
// Output: { _id: 'Duration', _tag: 'Millis', millis: 2000 }
console.log(decode(null))
/*
throws:
ParseError: Expected DurationFromSelf, actual null
*/
DurationFromMillis
把 number 转换为 Duration,其中的数字表示毫秒数。
示例
import { Schema } from "effect"
const schema = Schema.DurationFromMillis
// ┌─── number
// ▼
type Encoded = typeof schema.Encoded
// ┌─── Duration
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
// Decoding examples
console.log(decode(0))
// Output: { _id: 'Duration', _tag: 'Millis', millis: 0 }
console.log(decode(5000))
// Output: { _id: 'Duration', _tag: 'Millis', millis: 5000 }
DurationFromNanos
把 BigInt 转换为 Duration,其中的数字表示纳秒数。
示例
import { Schema } from "effect"
const schema = Schema.DurationFromNanos
// ┌─── bigint
// ▼
type Encoded = typeof schema.Encoded
// ┌─── Duration
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
// Decoding examples
console.log(decode(0n))
// Output: { _id: 'Duration', _tag: 'Millis', millis: 0 }
console.log(decode(5000000000n))
// Output: { _id: 'Duration', _tag: 'Nanos', hrtime: [ 5, 0 ] }
clampDuration
把 Duration 限制在最小值与最大值之间。
示例
import { Schema, Duration } from "effect"
const schema = Schema.DurationFromSelf.pipe(
Schema.clampDuration("5 seconds", "10 seconds"),
)
// ┌─── Duration
// ▼
type Encoded = typeof schema.Encoded
// ┌─── Duration
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
// Decoding examples
console.log(decode(Duration.decode("2 seconds")))
// Output: { _id: 'Duration', _tag: 'Millis', millis: 5000 }
console.log(decode(Duration.decode("6 seconds")))
// Output: { _id: 'Duration', _tag: 'Millis', millis: 6000 }
console.log(decode(Duration.decode("11 seconds")))
// Output: { _id: 'Duration', _tag: 'Millis', millis: 10000 }
Redacted
Redacted
Schema.Redacted 函数专门用于处理敏感信息,它把 string 转换为 Redacted 对象。
这种转换能确保敏感数据不会暴露在应用的输出中。
示例(基础的 Redacted schema)
import { Schema } from "effect"
const schema = Schema.Redacted(Schema.String)
// ┌─── string
// ▼
type Encoded = typeof schema.Encoded
// ┌─── Redacted<string>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
// Decoding examples
console.log(decode("keep it secret, keep it safe"))
// Output: <redacted>
需要注意的是,成功解码 Redacted 时,输出会被有意遮蔽为 <redacted>,以免真正的机密内容泄露到日志或控制台输出中。
把 Redacted schema 与其他 schema 组合使用时必须格外小心,因为解码或编码错误
有可能暴露敏感信息。
示例(错误期间的暴露风险)
在下面的示例中,如果输入字符串不符合条件(例如包含空格),生成的错误消息可能会无意中暴露输入中包含的敏感信息。
import { Schema } from "effect"
import { Redacted } from "effect"
const schema = Schema.Trimmed.pipe(
Schema.compose(Schema.Redacted(Schema.String)),
)
console.log(Schema.decodeUnknownEither(schema)(" SECRET"))
/*
{
_id: 'Either',
_tag: 'Left',
left: {
_id: 'ParseError',
message: '(Trimmed <-> (string <-> Redacted(<redacted>)))\n' +
'└─ Encoded side transformation failure\n' +
' └─ Trimmed\n' +
' └─ Predicate refinement failure\n' +
' └─ Expected Trimmed (a string with no leading or trailing whitespace), actual " SECRET"'
}
}
*/
console.log(Schema.encodeEither(schema)(Redacted.make(" SECRET")))
/*
{
_id: 'Either',
_tag: 'Left',
left: {
_id: 'ParseError',
message: '(Trimmed <-> (string <-> Redacted(<redacted>)))\n' +
'└─ Encoded side transformation failure\n' +
' └─ Trimmed\n' +
' └─ Predicate refinement failure\n' +
' └─ Expected Trimmed (a string with no leading or trailing whitespace), actual " SECRET"'
}
}
*/
缓解暴露风险
为了降低错误消息中敏感信息泄露的风险,你可以自定义错误消息,以遮蔽敏感细节:
示例(自定义错误消息)
import { Schema } from "effect"
import { Redacted } from "effect"
const schema = Schema.Trimmed.annotations({
message: () => "Expected Trimmed, actual <redacted>",
}).pipe(Schema.compose(Schema.Redacted(Schema.String)))
console.log(Schema.decodeUnknownEither(schema)(" SECRET"))
/*
{
_id: 'Either',
_tag: 'Left',
left: {
_id: 'ParseError',
message: '(Trimmed <-> (string <-> Redacted(<redacted>)))\n' +
'└─ Encoded side transformation failure\n' +
' └─ Expected Trimmed, actual <redacted>'
}
}
*/
console.log(Schema.encodeEither(schema)(Redacted.make(" SECRET")))
/*
{
_id: 'Either',
_tag: 'Left',
left: {
_id: 'ParseError',
message: '(Trimmed <-> (string <-> Redacted(<redacted>)))\n' +
'└─ Encoded side transformation failure\n' +
' └─ Expected Trimmed, actual <redacted>'
}
}
*/
RedactedFromSelf
Schema.RedactedFromSelf schema 用于校验给定值是否符合 effect 库中的 Redacted 类型。
示例
import { Schema } from "effect"
import { Redacted } from "effect"
const schema = Schema.RedactedFromSelf(Schema.String)
// ┌─── Redacted<string>
// ▼
type Encoded = typeof schema.Encoded
// ┌─── Redacted<string>
// ▼
type Type = typeof schema.Type
const decode = Schema.decodeUnknownSync(schema)
// Decoding examples
console.log(decode(Redacted.make("mysecret")))
// Output: <redacted>
console.log(decode(null))
/*
throws:
ParseError: Expected Redacted(<redacted>), actual null
*/
需要注意的是,成功解码一个 Redacted 时,输出会被有意遮蔽为(<redacted>),以防止真正的机密内容在日志或控制台输出中暴露出来。