diff --git a/src/Condition.ts b/src/Condition.ts index 6a3676d5e..27d7cc4cb 100644 --- a/src/Condition.ts +++ b/src/Condition.ts @@ -1,6 +1,6 @@ import isSchema from './util/isSchema'; import Reference from './Reference'; -import type { ISchema } from './types'; +import type { Ancester, ISchema } from './types'; export type ConditionBuilder> = ( values: any[], @@ -18,6 +18,7 @@ export type ResolveOptions = { value?: any; parent?: any; context?: TContext; + from?: Ancester[]; }; class Condition = ISchema> { diff --git a/src/index.ts b/src/index.ts index cd43c5d14..9d95089fe 100644 --- a/src/index.ts +++ b/src/index.ts @@ -13,7 +13,7 @@ import TupleSchema, { create as tupleCreate } from './tuple'; import { create as refCreate } from './Reference'; import { create as lazyCreate } from './Lazy'; import ValidationError from './ValidationError'; -import reach, { getIn } from './util/reach'; +import reach, { getIn, reachAndResolve } from './util/reach'; import isSchema from './util/isSchema'; import printValue from './util/printValue'; import setLocale, { LocaleObject } from './setLocale'; @@ -99,6 +99,7 @@ export { lazyCreate as lazy, tupleCreate as tuple, reach, + reachAndResolve, getIn, isSchema, printValue, diff --git a/src/object.ts b/src/object.ts index aaabfc61f..a61d6d16e 100644 --- a/src/object.ts +++ b/src/object.ts @@ -166,12 +166,17 @@ export default class ObjectSchema< let props = ([] as string[]).concat( this._nodes, Object.keys(value).filter((v) => !this._nodes.includes(v)), - ); + ); let intermediateValue: Record = {}; // is filled during the transform below + let intermediateFrom = options.from ? [ + {...options.from[0], value: intermediateValue }, + ...options.from.slice(1) + ] : []; let innerOptions: InternalOptions = { ...options, parent: intermediateValue, + from: intermediateFrom, __validating: options.__validating || false, }; @@ -191,6 +196,7 @@ export default class ObjectSchema< value: inputValue, context: options.context, parent: intermediateValue, + from: innerOptions.from }); let fieldSpec = field instanceof Schema ? field.spec : undefined; diff --git a/src/util/reach.ts b/src/util/reach.ts index c182d84f0..399444fc1 100644 --- a/src/util/reach.ts +++ b/src/util/reach.ts @@ -1,6 +1,6 @@ import { forEach } from 'property-expr'; import type Reference from '../Reference'; -import type { InferType, ISchema } from '../types'; +import type { Ancester, InferType, ISchema } from '../types'; import type { Get } from 'type-fest'; export function getIn( @@ -12,16 +12,22 @@ export function getIn( schema: ISchema | Reference; parent: any; parentPath: string; + from: Ancester[]; + value: any; } { - let parent: any, lastPart: string, lastPartDebug: string; + let parent: any, + parentSchema: any, + lastPart: string, + lastPartDebug: string, + from: Ancester[] = []; // root path: '' - if (!path) return { parent, parentPath: path, schema }; + if (!path) return { parent, parentPath: path, schema, from, value }; forEach(path, (_part, isBracket, isArray) => { let part = isBracket ? _part.slice(1, _part.length - 1) : _part; - schema = schema.resolve({ context, parent, value }); + schema = schema.resolve({ context, parent, value, from }); let isTuple = schema.type === 'tuple'; let idx = isArray ? parseInt(part, 10) : 0; @@ -54,15 +60,17 @@ export function getIn( ); parent = value; + parentSchema = schema; value = value && value[part]; schema = schema.fields[part]; + from = [{ schema: parentSchema, value: parent }, ...from]; } lastPart = part; lastPartDebug = isBracket ? '[' + _part + ']' : '.' + _part; }); - return { schema, parent, parentPath: lastPart! }; + return { schema, parent, parentPath: lastPart!, from, value }; } function reach

>( @@ -76,4 +84,21 @@ function reach

>( return getIn(obj, path, value, context).schema as any; } +export function reachAndResolve

>( + obj: S, + path: P, + value?: any, + context?: any, +): + | Reference, P>> + | ISchema, P>, S['__context']> { + const reachedSchema = getIn(obj, path, value, context); + return reachedSchema.schema.resolve({ + context, + parent: reachedSchema.parent, + value: reachedSchema.value, + from: reachedSchema.from, + }) as any; +} + export default reach;