Skip to content

Commit 95fe284

Browse files
committed
add type inference for middy middleware
1 parent 7c00d85 commit 95fe284

File tree

5 files changed

+198
-71
lines changed

5 files changed

+198
-71
lines changed

packages/parser/src/middleware/parser.ts

+10-5
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import { type MiddyLikeRequest } from '@aws-lambda-powertools/commons/types';
22
import { type MiddlewareObj } from '@middy/core';
3-
import { type ZodSchema } from 'zod';
4-
import { type ParserOptions } from '../types/parser.js';
3+
import { ZodType } from 'zod';
4+
import type { ParserOptions, ParseOutput } from '../types/parser.js';
55
import { parse } from '../parser.js';
6+
import type { Envelope } from '../types/envelope.js';
67

78
/**
89
* A middiy middleware to parse your event.
@@ -32,9 +33,13 @@ import { parse } from '../parser.js';
3233
*
3334
* @param options
3435
*/
35-
const parser = <S extends ZodSchema>(
36-
options: ParserOptions<S>
37-
): MiddlewareObj => {
36+
const parser = <
37+
TSchema extends ZodType,
38+
TEnvelope extends Envelope = undefined,
39+
TSafeParse extends boolean = false,
40+
>(
41+
options: ParserOptions<TSchema, TEnvelope, TSafeParse>
42+
): MiddlewareObj<ParseOutput<TSchema, TEnvelope, TSafeParse>> => {
3843
const before = (request: MiddyLikeRequest): void => {
3944
const { schema, envelope, safeParse } = options;
4045

packages/parser/src/parserDecorator.ts

+11-9
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
import type { HandlerMethodDecorator } from '@aws-lambda-powertools/commons/types';
22
import type { Context, Handler } from 'aws-lambda';
3-
import { ZodSchema, z } from 'zod';
3+
import { type ZodSchema } from 'zod';
44
import { parse } from './parser.js';
5-
import type { ParserOptions, ParsedResult } from './types/index.js';
5+
import type { ParserOptions, Envelope } from './types/index.js';
6+
import type { ParseOutput } from './types/parser.js';
67

78
/**
89
* A decorator to parse your event.
@@ -67,8 +68,12 @@ import type { ParserOptions, ParsedResult } from './types/index.js';
6768
*
6869
* @param options Configure the parser with the `schema`, `envelope` and whether to `safeParse` or not
6970
*/
70-
export const parser = <S extends ZodSchema>(
71-
options: ParserOptions<S>
71+
export const parser = <
72+
TSchema extends ZodSchema,
73+
TEnvelope extends Envelope = undefined,
74+
TSafeParse extends boolean = false,
75+
>(
76+
options: ParserOptions<TSchema, TEnvelope, TSafeParse>
7277
): HandlerMethodDecorator => {
7378
return (_target, _propertyKey, descriptor) => {
7479
const original = descriptor.value!;
@@ -77,14 +82,11 @@ export const parser = <S extends ZodSchema>(
7782

7883
descriptor.value = async function (
7984
this: Handler,
80-
event: unknown,
85+
event: ParseOutput<TSchema, TEnvelope, TSafeParse>,
8186
context: Context,
8287
callback
8388
) {
84-
const parsedEvent: ParsedResult<
85-
typeof event,
86-
z.infer<typeof schema>
87-
> = parse(event, envelope, schema, safeParse);
89+
const parsedEvent = parse(event, envelope, schema, safeParse);
8890

8991
return original.call(this, parsedEvent, context, callback);
9092
};

packages/parser/src/types/envelope.ts

+27-2
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,14 @@ import type {
1414
VpcLatticeEnvelope,
1515
VpcLatticeV2Envelope,
1616
} from '../envelopes/index.js';
17+
import { z, type ZodSchema } from 'zod';
1718

18-
export type Envelope =
19+
type DynamoDBStreamEnvelopeResponse<Schema extends ZodSchema> = {
20+
NewImage: z.infer<Schema>;
21+
OldImage: z.infer<Schema>;
22+
};
23+
24+
type Envelope =
1925
| typeof ApiGatewayEnvelope
2026
| typeof ApiGatewayV2Envelope
2127
| typeof CloudWatchEnvelope
@@ -29,4 +35,23 @@ export type Envelope =
2935
| typeof SnsSqsEnvelope
3036
| typeof SqsEnvelope
3137
| typeof VpcLatticeEnvelope
32-
| typeof VpcLatticeV2Envelope;
38+
| typeof VpcLatticeV2Envelope
39+
| undefined;
40+
41+
/**
42+
* Envelopes that return an array, needed to narrow down the return type of the parser
43+
*/
44+
type EnvelopeArrayReturnType =
45+
| typeof CloudWatchEnvelope
46+
| typeof DynamoDBStreamEnvelope
47+
| typeof KafkaEnvelope
48+
| typeof KinesisEnvelope
49+
| typeof KinesisFirehoseEnvelope
50+
| typeof SnsEnvelope
51+
| typeof SqsEnvelope;
52+
53+
export type {
54+
Envelope,
55+
DynamoDBStreamEnvelopeResponse,
56+
EnvelopeArrayReturnType,
57+
};

packages/parser/src/types/parser.ts

+47-6
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
1-
import type { ZodSchema, ZodError } from 'zod';
2-
import type { Envelope } from './envelope.js';
1+
import { type ZodSchema, type ZodError, z } from 'zod';
2+
import type { Envelope, EnvelopeArrayReturnType } from './envelope.js';
33

44
/**
55
* Options for the parser used in middy middleware and decorator
66
*/
7-
type ParserOptions<S extends ZodSchema> = {
8-
schema: S;
9-
envelope?: Envelope;
10-
safeParse?: boolean;
7+
type ParserOptions<
8+
TSchema extends ZodSchema,
9+
TEnvelope extends Envelope,
10+
TSafeParse extends boolean,
11+
> = {
12+
schema: TSchema;
13+
envelope?: TEnvelope;
14+
safeParse?: TSafeParse;
1115
};
1216

1317
/**
@@ -34,9 +38,46 @@ type ParsedResult<Input = unknown, Output = unknown> =
3438
| ParsedResultSuccess<Output>
3539
| ParsedResultError<Input>;
3640

41+
/**
42+
* The inferred result of the schema, can be either an array or a single object depending on the envelope
43+
*/
44+
type ZodInferredResult<
45+
TSchema extends ZodSchema,
46+
TEnvelope extends Envelope,
47+
> = undefined extends TEnvelope
48+
? z.infer<TSchema>
49+
: TEnvelope extends EnvelopeArrayReturnType
50+
? z.infer<TSchema>[]
51+
: z.infer<TSchema>;
52+
53+
type ZodInferredSafeParseResult<
54+
TSchema extends ZodSchema,
55+
TEnvelope extends Envelope,
56+
> = undefined extends TEnvelope
57+
? ParsedResult<unknown, z.infer<TSchema>>
58+
: TEnvelope extends EnvelopeArrayReturnType
59+
? ParsedResult<unknown, z.infer<TSchema>>
60+
: ParsedResult<unknown, z.infer<TSchema>[]>;
61+
62+
/**
63+
* The output of the parser function, can be either schema inferred type or a ParsedResult
64+
*/
65+
type ParseOutput<
66+
TSchema extends ZodSchema,
67+
TEnvelope extends Envelope,
68+
TSafeParse = false,
69+
> = undefined extends TSafeParse
70+
? ZodInferredResult<TSchema, TEnvelope>
71+
: TSafeParse extends true
72+
? ZodInferredSafeParseResult<TSchema, TEnvelope>
73+
: TSafeParse extends false
74+
? ZodInferredResult<TSchema, TEnvelope>
75+
: never;
76+
3777
export type {
3878
ParserOptions,
3979
ParsedResult,
4080
ParsedResultError,
4181
ParsedResultSuccess,
82+
ParseOutput,
4283
};

0 commit comments

Comments
 (0)