From 191105d2084337a5f5dac8231ece59aa9243abb1 Mon Sep 17 00:00:00 2001 From: Aarsh Duhlani Date: Thu, 25 Jun 2026 04:28:58 +0530 Subject: [PATCH 1/3] fix(type): wire up type.fn.raw at runtime type.fn.raw was typed but always undefined at runtime, so calling it threw "type.fn.raw is not a function". The parser read `$.fn` while building its `attach` object, but `$.fn` is the InternalFnParser instance currently being constructed, so it was still undefined and got captured as `raw`. Extract the parser into a local `parse` function and reference it for both the callable body and `raw` so there's no longer a dependency on the half-initialized scope. Closes #1609 --- ark/type/__tests__/fn.test.ts | 9 +++++++ ark/type/fn.ts | 48 ++++++++++++++++++----------------- 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/ark/type/__tests__/fn.test.ts b/ark/type/__tests__/fn.test.ts index e8763e54d9..df293dc3c9 100644 --- a/ark/type/__tests__/fn.test.ts +++ b/ark/type/__tests__/fn.test.ts @@ -315,6 +315,15 @@ contextualize(() => { attest(f.name).snap("bound typed originalName") }) + it("raw", () => { + const len = type.fn.raw("string | unknown[]")((s: string) => s.length) + + attest(len("foo")).equals(3) + attest(() => len(1)).throws.snap( + "TraversalError: value at [0] must be a string or an object (was a number)" + ) + }) + it("arg submodule completions", () => { // @ts-expect-error attest(() => type.fn("string.nu")).completions({ diff --git a/ark/type/fn.ts b/ark/type/fn.ts index 992eee80e8..39c0ef35da 100644 --- a/ark/type/fn.ts +++ b/ark/type/fn.ts @@ -73,35 +73,37 @@ type FnParserAttachments = Omit export class InternalFnParser extends Callable<(...args: unknown[]) => Fn> { constructor($: InternalScope) { - const attach: FnParserAttachments = { - $: $ as never, - raw: $.fn - } + const parse = (...signature: unknown[]) => { + const returnOperatorIndex = signature.indexOf(":") + const lastParamIndex = + returnOperatorIndex === -1 ? + signature.length - 1 + : returnOperatorIndex - 1 - super( - (...signature) => { - const returnOperatorIndex = signature.indexOf(":") - const lastParamIndex = - returnOperatorIndex === -1 ? - signature.length - 1 - : returnOperatorIndex - 1 + const paramDefs = signature.slice(0, lastParamIndex + 1) - const paramDefs = signature.slice(0, lastParamIndex + 1) + const paramTuple = $.parse(paramDefs).assertHasKind("intersection") - const paramTuple = $.parse(paramDefs).assertHasKind("intersection") + let returnType: BaseRoot = $.intrinsic.unknown + + if (returnOperatorIndex !== -1) { + if (returnOperatorIndex !== signature.length - 2) + return throwParseError(badFnReturnTypeMessage) + returnType = $.parse(signature[returnOperatorIndex + 1]) + } - let returnType: BaseRoot = $.intrinsic.unknown + return (impl: Fn) => new InternalTypedFn(impl, paramTuple, returnType) + } - if (returnOperatorIndex !== -1) { - if (returnOperatorIndex !== signature.length - 2) - return throwParseError(badFnReturnTypeMessage) - returnType = $.parse(signature[returnOperatorIndex + 1]) - } + // `raw` is an alias of `fn` itself with no type-level validation. It must + // reference `parse` directly rather than `$.fn`, which is still being + // constructed (and thus `undefined`) at this point. + const attach: FnParserAttachments = { + $: $ as never, + raw: parse + } - return (impl: Fn) => new InternalTypedFn(impl, paramTuple, returnType) - }, - { attach } - ) + super(parse, { attach }) } } From 9ba8bea566ffe6f6359407cd7247ebfee02ae5e3 Mon Sep 17 00:00:00 2001 From: Aarsh Duhlani Date: Wed, 1 Jul 2026 19:30:47 +0530 Subject: [PATCH 2/3] fix(type): cast raw attachment to satisfy RawFnParser --- ark/type/fn.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ark/type/fn.ts b/ark/type/fn.ts index 39c0ef35da..bd39724eed 100644 --- a/ark/type/fn.ts +++ b/ark/type/fn.ts @@ -100,7 +100,7 @@ export class InternalFnParser extends Callable<(...args: unknown[]) => Fn> { // constructed (and thus `undefined`) at this point. const attach: FnParserAttachments = { $: $ as never, - raw: parse + raw: parse as never } super(parse, { attach }) From 54dce18f1a070f606859aac051b08cf8666c3881 Mon Sep 17 00:00:00 2001 From: Aarsh Duhlani Date: Thu, 2 Jul 2026 17:14:44 +0530 Subject: [PATCH 3/3] test(type): annotate raw parser result which has no inference --- ark/type/__tests__/fn.test.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ark/type/__tests__/fn.test.ts b/ark/type/__tests__/fn.test.ts index df293dc3c9..7b03486444 100644 --- a/ark/type/__tests__/fn.test.ts +++ b/ark/type/__tests__/fn.test.ts @@ -316,7 +316,10 @@ contextualize(() => { }) it("raw", () => { - const len = type.fn.raw("string | unknown[]")((s: string) => s.length) + // raw has no type-level inference, so it's returned as an untyped parser + const len = type.fn.raw("string | unknown[]")((s: string) => s.length) as ( + data: unknown + ) => number attest(len("foo")).equals(3) attest(() => len(1)).throws.snap(