diff --git a/docs/typescript-examples/array.ts b/docs/typescript-examples/array.ts index c75226e..d9ada4a 100644 --- a/docs/typescript-examples/array.ts +++ b/docs/typescript-examples/array.ts @@ -1,5 +1,6 @@ /* eslint-disable no-console */ import * as de from '../../lib'; +import { blockNeedsA, blockNeedsB, ParamsA, ParamsAB } from './shared'; // --------------------------------------------------------------------------------------------------------------- // @@ -155,3 +156,28 @@ de.run(bfn3, { .then((result) => { console.log(result[ 0 ], result[ 1 ]); }); + +// --------------------------------------------------------------------------------------------------------------- // +// Тут проверяем, что есть ошибка при несовпадении параметров и нету ошибки при валидных параметрах вложенных блоков +// @ts-expect-error тут должна быть DescriptParamsError +de.func({ + block: ({ params }: { params: ParamsA }) => { + void params; + + return de.array({ + block: [ blockNeedsA, blockNeedsB ], + }); + }, +}); + +de.func({ + block: ({ params }: { params: ParamsAB }) => { + void params; + + return de.array({ + block: [ blockNeedsA, blockNeedsB ], + }); + }, +}); + +// --------------------------------------------------------------------------------------------------------------- // diff --git a/docs/typescript-examples/first.ts b/docs/typescript-examples/first.ts index 1b6e61f..7a7ef13 100644 --- a/docs/typescript-examples/first.ts +++ b/docs/typescript-examples/first.ts @@ -1,5 +1,6 @@ /* eslint-disable no-console */ import * as de from '../../lib'; +import { blockNeedsA, blockNeedsB, ParamsA, ParamsAB } from './shared'; // --------------------------------------------------------------------------------------------------------------- // @@ -164,3 +165,28 @@ de.run(bfn3, { .then((result) => { console.log(result); }); + +// --------------------------------------------------------------------------------------------------------------- // +// Тут проверяем, что есть ошибка при несовпадении параметров и нету ошибки при валидных параметрах вложенных блоков +// @ts-expect-error тут должна быть DescriptParamsError +de.func({ + block: ({ params }: { params: ParamsA }) => { + void params; + + return de.first({ + block: [ blockNeedsA, blockNeedsB ], + }); + }, +}); + +de.func({ + block: ({ params }: { params: ParamsAB }) => { + void params; + + return de.first({ + block: [ blockNeedsA, blockNeedsB ], + }); + }, +}); + +// --------------------------------------------------------------------------------------------------------------- // diff --git a/docs/typescript-examples/func.ts b/docs/typescript-examples/func.ts index 48c70d4..0ec771a 100644 --- a/docs/typescript-examples/func.ts +++ b/docs/typescript-examples/func.ts @@ -1,5 +1,6 @@ /* eslint-disable no-console */ import * as de from '../../lib'; +import { blockNeedsA, blockNeedsAB, blockNeedsB, ParamsA, ParamsAB } from './shared'; interface ParamsIn1 { id: string; @@ -126,3 +127,27 @@ de.run(block4, { .then((result) => { console.log(result); }); + +// --------------------------------------------------------------------------------------------------------------- // +// Тут проверяем, что есть ошибка при несовпадении параметров и нету ошибки при валидных параметрах вложенных блоков +// @ts-expect-error тут должна быть DescriptParamsError +de.func({ + block: ({ params }: { params: ParamsA }) => + params.a === 'special' ? blockNeedsA : blockNeedsB, +}); + +de.func({ + block: ({ params }: { params: ParamsAB }) => + params.a === 'special' ? blockNeedsA : blockNeedsB, +}); + +de.run(blockNeedsAB, { + params: { a: 'hello', b: 42 }, +}); + +de.run(blockNeedsAB, { + // @ts-expect-error тут должна быть ошибка 'b' is declared here. + params: { a: 'hello' }, +}); + +// --------------------------------------------------------------------------------------------------------------- // diff --git a/docs/typescript-examples/object.ts b/docs/typescript-examples/object.ts index d5ed514..1e40123 100644 --- a/docs/typescript-examples/object.ts +++ b/docs/typescript-examples/object.ts @@ -2,6 +2,7 @@ import * as de from '../../lib'; import type { DescriptHttpBlockResult, InferParamsInFromBlock } from '../../lib/types'; +import { blockNeedsA, blockNeedsB, ParamsA, ParamsAB } from './shared'; // --------------------------------------------------------------------------------------------------------------- // @@ -96,7 +97,7 @@ const block2 = de.http({ const block2Func = de.func({ // eslint-disable-next-line @typescript-eslint/no-unused-vars - block: ({ params }: { params: InferParamsInFromBlock & { p1: number } }) => block2, + block: ({ params }: { params: InferParamsInFromBlock & { p1: number } }) => block2, // block: () => block2, options: { after: ({ result }) => { @@ -111,11 +112,8 @@ const block2Func = de.func({ de.run(block2Func, { params: { - id1: '67890', p1: 1, - payload: { - card: {}, - }, + id2: 578923, }, }) .then((result) => { @@ -166,12 +164,8 @@ const block3 = de.object({ de.run(block3, { params: { - id1: '12345', id2: 67890, p1: 1, - payload: { - card: {}, - }, }, }) .then((result) => { @@ -230,12 +224,8 @@ const block5 = block3.extend({ de.run(block4, { params: { - id1: '12345', id2: 67890, p1: 1, - payload: { - card: {}, - }, }, }) .then((result) => { @@ -244,14 +234,35 @@ de.run(block4, { de.run(block5, { params: { - id1: '12345', id2: 67890, p1: 1, - payload: { - card: {}, - }, }, }) .then((result) => { console.log(result); }); + +// --------------------------------------------------------------------------------------------------------------- // +// Тут проверяем, что есть ошибка при несовпадении параметров и нету ошибки при валидных параметрах вложенных блоков +// @ts-expect-error тут должна быть DescriptParamsError +de.func({ + block: ({ params }: { params: ParamsA }) => { + void params; + + return de.object({ + block: { blockNeedsA, blockNeedsB }, + }); + }, +}); + +de.func({ + block: ({ params }: { params: ParamsAB }) => { + void params; + + return de.object({ + block: { blockNeedsA, blockNeedsB }, + }); + }, +}); + +// --------------------------------------------------------------------------------------------------------------- // diff --git a/docs/typescript-examples/pipe.ts b/docs/typescript-examples/pipe.ts index ccd1177..4c8bd34 100644 --- a/docs/typescript-examples/pipe.ts +++ b/docs/typescript-examples/pipe.ts @@ -1,5 +1,6 @@ /* eslint-disable no-console */ import * as de from '../../lib'; +import { blockNeedsA, blockNeedsB, ParamsA, ParamsAB } from './shared'; // --------------------------------------------------------------------------------------------------------------- // @@ -164,3 +165,28 @@ de.run(bfn3, { .then((result) => { console.log(result); }); + +// --------------------------------------------------------------------------------------------------------------- // +// Тут проверяем, что есть ошибка при несовпадении параметров и нету ошибки при валидных параметрах вложенных блоков +// @ts-expect-error тут должна быть DescriptParamsError +de.func({ + block: ({ params }: { params: ParamsA }) => { + void params; + + return de.pipe({ + block: [ blockNeedsA, blockNeedsB ], + }); + }, +}); + +de.func({ + block: ({ params }: { params: ParamsAB }) => { + void params; + + return de.pipe({ + block: [ blockNeedsA, blockNeedsB ], + }); + }, +}); + +// --------------------------------------------------------------------------------------------------------------- // diff --git a/docs/typescript-examples/shared.ts b/docs/typescript-examples/shared.ts new file mode 100644 index 0000000..8eb240e --- /dev/null +++ b/docs/typescript-examples/shared.ts @@ -0,0 +1,32 @@ +import * as de from '../../lib'; + +export interface ParamsA { + a: string; +} + +export interface ParamsB { + b: number; +} + +export interface ParamsAB { + a: string; + b: number; +} + +export const blockNeedsA = de.http({ + block: { + pathname: ({ params }: { params: ParamsA }) => `/a/${ params.a }`, + }, +}); + +export const blockNeedsB = de.http({ + block: { + pathname: ({ params }: { params: ParamsB }) => `/b/${ params.b }`, + }, +}); + +export const blockNeedsAB = de.http({ + block: { + pathname: ({ params }: { params: ParamsAB }) => `/b/${ params.b }/${ params.a }`, + }, +}); diff --git a/lib/index.ts b/lib/index.ts index b87505a..a1c745d 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -28,6 +28,7 @@ import type { InferBlock, InferHttpBlock, InferResultOrResult, + ExtractBadNestedParams, } from './types'; import type BaseBlock from './block'; import type { DescriptHttpBlockDescription, DescriptHttpBlockQuery, DescriptHttpBlockQueryValue } from './httpBlock'; @@ -51,7 +52,7 @@ const func = function< options?: DescriptBlockOptions< Context, ParamsOut, BlockResult, BeforeResultOut, AfterResultOut, ErrorResultOut, Params >; -}) { +} & ([ ExtractBadNestedParams ] extends [ never ] ? unknown : ExtractBadNestedParams)) { return new FunctionBlock< Context, ParamsOut, BlockResult, ResultOut, BeforeResultOut, AfterResultOut, ErrorResultOut, Params >({ block, options }); @@ -68,7 +69,7 @@ const array = function< Params = GetArrayBlockParams, >({ block, options }: { block: ArrayBlockDefinition; - options?: DescriptBlockOptions; + options?: DescriptBlockOptions, BlockResult, BeforeResultOut, AfterResultOut, ErrorResultOut, Params>; }) { return new ArrayBlock({ block, options }); }; @@ -85,7 +86,7 @@ const object = function< Params = GetObjectBlockParams, >({ block, options }: { block?: ObjectBlockDefinition; - options?: DescriptBlockOptions; + options?: DescriptBlockOptions, BlockResult, BeforeResultOut, AfterResultOut, ErrorResultOut, Params>; } = {}) { return new ObjectBlock({ block, options }); }; @@ -121,7 +122,7 @@ const first = function< Params = GetFirstBlockParams, >({ block, options }: { block: FirstBlockDefinition; - options?: DescriptBlockOptions; + options?: DescriptBlockOptions, BlockResult, BeforeResultOut, AfterResultOut, ErrorResultOut, Params>; }) { return new FirstBlock({ block, options }); }; @@ -138,7 +139,7 @@ const pipe = function< Params = GetPipeBlockParams, >({ block, options }: { block: PipeBlockDefinition; - options?: DescriptBlockOptions; + options?: DescriptBlockOptions, BlockResult, BeforeResultOut, AfterResultOut, ErrorResultOut, Params>; }) { return new PipeBlock({ block, options }); }; diff --git a/lib/types.ts b/lib/types.ts index 57aea6c..4700502 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -41,6 +41,27 @@ export type NonNullableObject> = { [P in keyof T]: Exclude; }; +export type DescriptParamsError = { + readonly __descriptError: 'NESTED_BLOCK_PARAMS_INCOMPATIBLE'; + readonly __requiredParams: Required; + readonly __availableParams: Available; + readonly __fix: 'Add options.params to transform parent params into the required shape'; +}; + +// Extracts DescriptParamsError for any block in T whose Params are not satisfied by the available Params. +// Returns never when all blocks are compatible (no constraint added to the call site). +export type ExtractBadNestedParams = + [ unknown ] extends [ Params ] + ? never + // eslint-disable-next-line @typescript-eslint/no-explicit-any + : T extends BaseBlock + ? unknown extends BParams + ? never + : [ Params ] extends [ BParams ] + ? never + : DescriptParamsError + : never; + export type DescriptJSON = boolean | number |