arktypeio / arktype

TypeScript's 1:1 validator, optimized from editor to runtime
https://arktype.io/
MIT License
3.9k stars 59 forks source link

Fix scope tuple expression functional inference #577

Open ssalbdivad opened 1 year ago

ssalbdivad commented 1 year ago

There is a problem inferring tuple expressions that reference an object in a scope. Based on some investigation, it has to do with aliases being passed to validateDefinition and an object type being parsed as the input definition.

Can be reproed with this test:

    it("functional inference in scope", () => {
        const bad = scope({
            a: [{ a: "1" }, "=>", data => `${data}`],
            // should be narrowed from {a: number} to {a: 1} but isn't
            b: [{ a: "number" }, "=>", (data): data is { a: 1 } => true]
        }).export()
        // @ts-expect-error inferred as never
        attest<string>(bad.a.infer)

        // works fine if input def is not a record or an alias resolving to a
        // record.
        const ok = scope({
            a: ["number", "=>", data => `${data}`],
            b: [["string"], "=>", data => data]
        }).export()
        attest<number>(ok.a.infer)
        attest<[string]>(ok.b.infer)

        // original form works fine for types
        const okType = type({
            a: [{ a: "1" }, "=>", data => `${data}`]
        })
        attest<{ a: string }>(okType.infer)
    })
ssalbdivad commented 3 months ago

A more recent example from #1034:

import { scope, type } from "arktype";

const t = type([{ a: "number" }, "=>", (obj) => ({ ...obj, b: obj.a + 3 })]); // fine (is typed correctly)
//    ^?

const Event = scope({ Event: [{ a: "number" }, "=>", (obj) => ({ ...obj, b: obj.a + 3 })] }).export().Event // not fine (is typed as `never` -- NB: works as-expected at runtime)
//    ^?