Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: misc #164

Merged
merged 2 commits into from
Jun 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions codecs/compact.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Codec, CodecVisitor, createCodec, metadata, ScaleDecodeError, withMetadata } from "../common/mod.ts"
import { AnyCodec } from "../mod.ts"
import { constant } from "./constant.ts"
import { u128, u16, u256, u32, u64, u8 } from "./int.ts"
import { field, object } from "./object.ts"
Expand All @@ -8,10 +9,10 @@ const MAX_U6 = 0b00111111
const MAX_U14 = 0b00111111_11111111
const MAX_U30 = 0b00111111_11111111_11111111_11111111

export const compactVisitor = new CodecVisitor<Codec<any>>()
export const compactVisitor = new CodecVisitor<AnyCodec>()

export function compact<T>(codec: Codec<T>): Codec<T> {
return compactVisitor.visit(codec)
export function compact<I, O>(codec: Codec<I, O>): Codec<I, O> {
return compactVisitor.visit(codec) as any
}

function compactNumber($base: Codec<number>): Codec<number> {
Expand Down Expand Up @@ -120,10 +121,10 @@ compactVisitor.add(constant<any>, (codec) => codec)
compactVisitor.add(tuple<any[]>, (codec, ...entries) => {
if (entries.length === 0) return codec
if (entries.length > 1) throw new Error("Cannot derive compact codec for tuples with more than one field")
return withMetadata(metadata("$.compact", compact<any>, codec), tuple(compact(entries[0]!)))
return withMetadata(metadata("$.compact", compact<any, any>, codec), tuple(compact(entries[0]!)))
})

compactVisitor.add(field<any, any>, (codec, key, value) => {
compactVisitor.add(field<any, any, any>, (codec, key, value) => {
return withMetadata(metadata("$.compact", compact, codec), field(key, compact(value)))
})

Expand Down
10 changes: 5 additions & 5 deletions codecs/instance.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { AssertState } from "../common/assert.ts"
import { Codec, createCodec, metadata } from "../common/mod.ts"

export function instance<A extends unknown[], O extends I, I = O>(
ctor: new(...args: A) => O,
$args: Codec<A>,
toArgs: (value: I) => [...A],
export function instance<AI extends readonly unknown[], AO extends readonly unknown[], I, O>(
ctor: new(...args: AO) => O,
$args: Codec<AI, AO>,
toArgs: (value: I) => [...AI],
): Codec<I, O> {
return createCodec({
_metadata: metadata("$.instance", instance, ctor, $args, toArgs),
Expand All @@ -17,7 +17,7 @@ export function instance<A extends unknown[], O extends I, I = O>(
},
_assert(assert) {
assert.instanceof(this, ctor)
$args._assert(new AssertState(toArgs(assert.value as O), "#arguments", assert))
$args._assert(new AssertState(toArgs(assert.value as I), "#arguments", assert))
},
})
}
13 changes: 7 additions & 6 deletions codecs/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import { AnyCodec, Codec, CodecVisitor, createCodec, Expand, Input, metadata, Ou
import { constant } from "./constant.ts"
import { option } from "./option.ts"

export function field<K extends keyof any, V>(key: K, $value: Codec<V>): Codec<
Expand<Readonly<Record<K, V>>>,
Expand<Record<K, V>>
export function field<K extends keyof any, VI, VO>(key: K, $value: Codec<VI, VO>): Codec<
Expand<Readonly<Record<K, VI>>>,
Expand<Record<K, VO>>
> {
return createCodec({
_metadata: metadata("$.field", field, key, $value),
Expand All @@ -21,9 +21,9 @@ export function field<K extends keyof any, V>(key: K, $value: Codec<V>): Codec<
})
}

export function optionalField<K extends keyof any, V>(key: K, $value: Codec<V>): Codec<
Expand<Readonly<Partial<Record<K, V>>>>,
Expand<Partial<Record<K, V>>>
export function optionalField<K extends keyof any, VI, VO>(key: K, $value: Codec<VI, VO>): Codec<
Expand<Readonly<Partial<Record<K, VI>>>>,
Expand<Partial<Record<K, VO>>>
> {
const $option = option($value)
return createCodec({
Expand Down Expand Up @@ -70,6 +70,7 @@ type UnionKeys<T> = T extends T ? keyof T : never
export type ObjectMembers<T extends AnyCodec[]> = [
...never extends T ? {
[K in keyof T]:
AnyCodec extends T[K] ? AnyCodec :
& UnionKeys<Input<T[K]>>
& {
[L in keyof T]: K extends L ? never : UnionKeys<Input<T[L]>>
Expand Down
2 changes: 1 addition & 1 deletion codecs/test/instance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ const $myError = $.withMetadata(
$.field("c", $.bool),
),
),
(myError) => [myError.code, myError.message, myError.payload],
(myError: MyError) => [myError.code, myError.message, myError.payload],
),
)

Expand Down
2 changes: 1 addition & 1 deletion codecs/test/result.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ class StrErr extends Error {
}
}

const $strError = $.instance(StrErr, $.tuple($.str), (err) => [err.str])
const $strError = $.instance(StrErr, $.tuple($.str), (err: StrErr) => [err.str])

testCodec($.result($.str, $strError), [
"ok",
Expand Down
4 changes: 2 additions & 2 deletions codecs/tuple.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { AnyCodec, Codec, createCodec, Input, metadata, Output } from "../common/mod.ts"

type InputTuple<T extends AnyCodec[]> = {
export type InputTuple<T extends AnyCodec[]> = {
readonly [K in keyof T]: Input<T[K]>
}
type OutputTuple<T extends AnyCodec[]> = {
export type OutputTuple<T extends AnyCodec[]> = {
[K in keyof T]: Output<T[K]>
}

Expand Down
20 changes: 11 additions & 9 deletions codecs/union.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ export class Variant<T extends string, I, O> {
constructor(readonly tag: T, readonly codec: Codec<I, O>) {}
}

export type AnyVariant = Variant<any, never, unknown>

export function variant<T extends string, E extends AnyCodec[]>(
tag: T,
...members: ObjectMembers<E>
Expand All @@ -16,29 +18,29 @@ export function variant<T extends string, E extends AnyCodec[]>(

export type InputTaggedUnion<
K extends keyof any,
M extends Record<number, Variant<any, any, any>>,
M extends Record<number, AnyVariant>,
> = {
[I in keyof M]: Expand<
& Readonly<Record<K, Extract<M[I], Variant<any, any, any>>["tag"]>>
& Input<Extract<M[I], Variant<any, any, any>>["codec"]>
& Readonly<Record<K, Extract<M[I], AnyVariant>["tag"]>>
& Input<Extract<M[I], AnyVariant>["codec"]>
>
}[keyof M & number]
export type OutputTaggedUnion<
K extends keyof any,
M extends Record<number, Variant<any, any, any>>,
M extends Record<number, AnyVariant>,
> = {
[I in keyof M]: Expand<
& Record<K, Extract<M[I], Variant<any, any, any>>["tag"]>
& Output<Extract<M[I], Variant<any, any, any>>["codec"]>
& Record<K, Extract<M[I], AnyVariant>["tag"]>
& Output<Extract<M[I], AnyVariant>["codec"]>
>
}[keyof M & number]

export function taggedUnion<
K extends keyof any,
M extends [] | Record<number, Variant<any, any, any>>,
M extends [] | Record<number, Variant<any, never, unknown>>,
>(tagKey: K, members: M): Codec<InputTaggedUnion<K, M>, OutputTaggedUnion<K, M>> {
const tagToDiscriminant: Record<string, number> = Object.create(null)
const discriminantToMember: Record<number, Codec<any>> = Object.create(null)
const discriminantToMember: Record<number, AnyCodec> = Object.create(null)
for (const _discriminant in members) {
const discriminant = +_discriminant
if (isNaN(discriminant)) continue
Expand All @@ -61,7 +63,7 @@ export function taggedUnion<
if (!$member) {
throw new ScaleDecodeError(this, buffer, `No such member codec matching the discriminant \`${discriminant}\``)
}
return $member._decode(buffer)
return $member._decode(buffer) as any
},
_assert(assert) {
const assertTag = assert.key(this, tagKey)
Expand Down
4 changes: 2 additions & 2 deletions common/codec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export function withMetadata<I, O>(metadata: Metadata<NoInfer<I>, NoInfer<O>>, c
return result
}

const codecInspectCtx = new Map<Codec<any>, number | null>()
const codecInspectCtx = new Map<AnyCodec, number | null>()
let codecInspectIdN = 0
const nodeCustomInspect = Symbol.for("nodejs.util.inspect.custom")
const denoCustomInspect = Symbol.for("Deno.customInspect")
Expand All @@ -51,7 +51,7 @@ abstract class _Codec {

// Properly handles circular codecs in the case of $.deferred
private _inspect(inspect: (value: unknown) => string): string
private _inspect<T>(this: Codec<T>, inspect: (value: unknown) => string): string {
private _inspect(this: AnyCodec, inspect: (value: unknown) => string): string {
let id = codecInspectCtx.get(this)
if (id !== undefined) {
if (id === null) {
Expand Down
10 changes: 5 additions & 5 deletions common/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,11 @@ export function docs<I = any, O = any>(docs: string): Metadata<I, O> {
}

export class CodecVisitor<R> {
#fallback?: <T>(codec: Codec<T>) => R
#fallback?: <I, O>(codec: Codec<I, O>) => R
#visitors = new Map<Metadata<any, any>[number] | Function, (codec: Codec<any>, ...args: any[]) => R>()

add<T, A extends unknown[]>(codec: (...args: A) => Codec<T>, fn: (codec: Codec<T>, ...args: A) => R): this
add<T>(codec: Codec<T>, fn: (codec: Codec<T>) => R): this
add<I, O, A extends unknown[]>(codec: (...args: A) => Codec<I, O>, fn: (codec: Codec<I, O>, ...args: A) => R): this
add<I, O>(codec: Codec<I, O>, fn: (codec: Codec<I, O>) => R): this
add(codec: Codec<any> | Metadata<any, any>[number] | Function, fn: (codec: Codec<any>, ...args: any[]) => R): this {
if (codec instanceof Codec) {
codec = codec._metadata[0]!
Expand All @@ -81,7 +81,7 @@ export class CodecVisitor<R> {
return this
}

fallback(fn: <T>(codec: Codec<T>) => R): this {
fallback(fn: <I, O>(codec: Codec<I, O>) => R): this {
if (this.#fallback) {
throw new Error("Duplicate fallback")
}
Expand All @@ -103,7 +103,7 @@ export class CodecVisitor<R> {
return this
}

visit<T>(codec: Codec<T>): R {
visit<I, O>(codec: Codec<I, O>): R {
for (const metadata of codec._metadata) {
let visitor = this.#visitors.get(metadata)
if (visitor) return visitor(codec)
Expand Down
2 changes: 1 addition & 1 deletion examples/objects.eg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ class MyError extends Error {
export const $myError = $.instance(
MyError,
$.tuple($.u8, $.str), // Specify how to encode/decode constructor arguments
(myError) => [myError.code, myError.message], // Specify how to extract arguments from an instance
(myError: MyError) => [myError.code, myError.message], // Specify how to extract arguments from an instance
)

$myError // Codec<MyError>
Loading