Skip to content

Commit

Permalink
efficient noinfer
Browse files Browse the repository at this point in the history
  • Loading branch information
ssalbdivad committed Sep 17, 2024
1 parent 69298e4 commit 7b521ff
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 50 deletions.
38 changes: 38 additions & 0 deletions ark/type/__tests__/objects/mapped.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,44 @@ contextualize(() => {
attest(t.expression).equals(expected.expression)
})

it("filter and split values", () => {
const original = type({
"foo?": "string",
bar: "number",
baz: {
inner: "string"
}
})

const getInner = (data: typeof original.infer.baz) => data.inner

const t = original.map(prop => {
if (prop.key === "bar") return []

if (prop.key === "baz") {
return [
prop,
{
key: "fromBaz" as const,
value: prop.value.pipe(getInner)
}
]
}
return prop
})

const expected = type({
"foo?": "string",
baz: {
inner: "string"
},
fromBaz: original.get("baz").pipe(getInner)
})

attest<typeof expected>(t)
attest(t.expression).equals(expected.expression)
})

it("change optionality", () => {
const original = type({
"foo?": "string",
Expand Down
4 changes: 3 additions & 1 deletion ark/type/__tests__/realWorld.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -881,10 +881,12 @@ nospace must be matched by ^\\S*$ (was "One space")`)
attest(out.toString()).snap("name must be removed or city must be removed")
})

it("array intersection", () => {
it("array intersection with object literal", () => {
const t = type({ name: "string" }).and("string[]")
attest(t).type.toString.snap("Type<{ name: string } & string[], {}>")
attest(t.infer).type.toString.snap("{ name: string } & string[]")

attest(t.expression).snap("{ name: string } & string[]")
})

it("tuple or morph inference", () => {
Expand Down
3 changes: 3 additions & 0 deletions ark/type/generic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,9 @@ export type GenericInstantiator<
> =
params["length"] extends 1 ?
{
// precomputing the results as default parameters is more efficient
// here than inferring them into aliases conditionally as we do in
// some of type's methods
<const a, r = instantiateGeneric<def, params, [a], $, args$>>(
a: type.validate<a, args$> & validateGenericArg<a, params[0], args$>
): r
Expand Down
17 changes: 8 additions & 9 deletions ark/type/keywords/inference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,17 +314,16 @@ type inferredOptionalOrDefaultKeyOf<o> =
| inferredDefaultKeyOf<o>
| inferredOptionalKeyOf<o>

type inferredDefaultKeyOf<o> = {
[k in keyof o]: [o[k]] extends [anyOrNever] ? never
type inferredDefaultKeyOf<o> = keyof {
[k in keyof o as [o[k]] extends [anyOrNever] ? never
: o[k] extends InferredDefault ? k
: never
}[keyof o]

type inferredOptionalKeyOf<o> = {
[k in keyof o]: [o[k]] extends [anyOrNever] ? never
: never]: never
}
type inferredOptionalKeyOf<o> = keyof {
[k in keyof o as [o[k]] extends [anyOrNever] ? never
: o[k] extends InferredOptional ? k
: never
}[keyof o]
: never]: never
}

type distillArray<t extends array, opts extends distill.Options> =
_distillArray<[...t], opts, []> extends infer result ?
Expand Down
62 changes: 33 additions & 29 deletions ark/type/methods/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,15 +63,6 @@ interface Type<out t = unknown, $ = {}>

describe(description: string): this

optional<r = applyConstraint<t, Optional>>(): instantiateType<r, $>

default<
value extends this["infer"],
r = (In?: this["inferBrandableIn"]) => Default<value>
>(
value: value
): instantiateType<r, $>

onUndeclaredKey(behavior: UndeclaredKeyBehavior): this

onDeepUndeclaredKey(behavior: UndeclaredKeyBehavior): this
Expand All @@ -83,24 +74,24 @@ interface Type<out t = unknown, $ = {}>
get in(): instantiateType<this["inferBrandableIn"], $>
get out(): instantiateType<this["inferIntrospectableOut"], $>

// these defaulted params are split up to optimize
// type perf while maintaining accurate inference for test cases
// like "nested 'and' chained from morph on optional".

// NoInfer avoids return type inference that can lead to incorrect results.
// It should be added to all defaulted params.
// See: https://discord.com/channels/957797212103016458/1285420361415917680/1285545752172429312
intersect<const def, r = type.infer<def, $>>(
// inferring r into an alias improves perf and avoids return type inference
// that can lead to incorrect results. See:
// https://discord.com/channels/957797212103016458/1285420361415917680/1285545752172429312
intersect<const def>(
def: type.validate<def, $>
): instantiateType<inferIntersection<t, NoInfer<r>>, $> | Disjoint
): type.infer<def, $> extends infer r ?
instantiateType<inferIntersection<t, r>, $> | Disjoint
: never

and<const def, r = type.infer<def, $>>(
and<const def>(
def: type.validate<def, $>
): instantiateType<inferIntersection<t, NoInfer<r>>, $>
): type.infer<def, $> extends infer r ?
instantiateType<inferIntersection<t, r>, $>
: never

or<const def, r = type.infer<def, $>>(
or<const def>(
def: type.validate<def, $>
): instantiateType<t | NoInfer<r>, $>
): type.infer<def, $> extends infer r ? instantiateType<t | r, $> : never

array(): ArrayType<t[], $>

Expand All @@ -110,31 +101,44 @@ interface Type<out t = unknown, $ = {}>

ifEquals<const def>(
def: type.validate<def, $>
): instantiateType<type.infer<def, $>, $> | undefined
): type.infer<def, $> extends infer r ? instantiateType<r, $> | undefined
: never

extends<const def>(
other: type.validate<def, $>
): this is instantiateType<type.infer<def, $>, $>
extends<const def>(other: type.validate<def, $>): boolean

ifExtends<const def>(
other: type.validate<def, $>
): instantiateType<type.infer<def, $>, $> | undefined
): type.infer<def, $> extends infer r ? instantiateType<r, $> | undefined
: never

overlaps<const def>(r: type.validate<def, $>): boolean

extract<const def>(
r: type.validate<def, $>
): instantiateType<Extract<t, type.infer<def, $>>, $>
): type.infer<def, $> extends infer r ? instantiateType<Extract<t, r>, $>
: never

exclude<const def>(
r: type.validate<def, $>
): instantiateType<Exclude<t, type.infer<def, $>>, $>
): type.infer<def, $> extends infer r ? instantiateType<Exclude<t, r>, $>
: never

distribute<mapOut, reduceOut = mapOut[]>(
mapBranch: (branch: Type, i: number, branches: array<Type>) => mapOut,
reduceMapped?: (mappedBranches: mapOut[]) => reduceOut
): NoInfer<reduceOut>

// inferring r into an alias in the return doesn't
// work the way it does for the other methods here
optional<r = applyConstraint<t, Optional>>(): instantiateType<r, $>

default<
value extends this["infer"],
r = (In?: this["inferBrandableIn"]) => Default<value>
>(
value: value
): instantiateType<r, $>

// deprecate Function methods so they are deprioritized as suggestions

/** @deprecated */
Expand Down
19 changes: 8 additions & 11 deletions ark/type/methods/object.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,27 +37,24 @@ interface Type<out t extends object = object, $ = {}>

keyof(): instantiateType<arkKeyOf<t>, $>

get<k1 extends arkKeyOf<t>, r = instantiateType<arkGet<t, k1>, $>>(
get<k1 extends arkKeyOf<t>>(
k1: k1 | type.cast<k1>
): NoInfer<r>
get<
k1 extends arkKeyOf<t>,
k2 extends arkKeyOf<arkGet<t, k1>>,
r = instantiateType<arkGet<arkGet<t, k1>, k2>, $>
>(
): instantiateType<arkGet<t, k1>, $> extends infer r ? r : never
get<k1 extends arkKeyOf<t>, k2 extends arkKeyOf<arkGet<t, k1>>>(
k1: k1 | type.cast<k1>,
k2: k2 | type.cast<k2>
): NoInfer<r>
): instantiateType<arkGet<arkGet<t, k1>, k2>, $> extends infer r ? r : never
get<
k1 extends arkKeyOf<t>,
k2 extends arkKeyOf<arkGet<t, k1>>,
k3 extends arkKeyOf<arkGet<arkGet<t, k1>, k2>>,
r = instantiateType<arkGet<arkGet<arkGet<t, k1>, k2>, k3>, $>
k3 extends arkKeyOf<arkGet<arkGet<t, k1>, k2>>
>(
k1: k1 | type.cast<k1>,
k2: k2 | type.cast<k2>,
k3: k3 | type.cast<k3>
): NoInfer<r>
): instantiateType<arkGet<arkGet<arkGet<t, k1>, k2>, k3>, $> extends infer r ?
r
: never

pick<const key extends arkKeyOf<t> = never>(
...keys: (key | type.cast<key>)[]
Expand Down

0 comments on commit 7b521ff

Please sign in to comment.