diff --git a/ark/type/__tests__/fn.test.ts b/ark/type/__tests__/fn.test.ts index e8763e54d..df293dc3c 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 992eee80e..39c0ef35d 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 }) } }