config icon indicating copy to clipboard operation
config copied to clipboard

TypeScript type checking inconsistency with ConfigService Zod validation

Open Miguiz opened this issue 2 months ago • 3 comments

Is there an existing issue for this?

  • [x] I have searched the existing issues

Current behavior

I found a type checking inconsistency in @nestjs/config when using ConfigService with Zod schema containing optional fields.

The ValidatedResult type is defined as:

type ValidatedResult<WasValidated extends boolean, T> = 
    WasValidated extends true ? T : T | undefined;

When using ConfigService with a Zod schema containing optional fields, TypeScript behaves inconsistently:

// Zod schema
const schema = z.object({
    API_KEY: z.string().optional()  // Type: string | undefined
});
type Config = z.infer<typeof schema>;

// Service
class Service {
    private apiKey: string;  // Note: strict type string
    
    constructor(private config: ConfigService<Config, true>) {
        // Should error: assigning string | undefined to string
        this.apiKey = this.config.get('API_KEY', { infer: true });  // No TypeScript error!
    }
}

Current behavior:

  • With ConfigService<Config, true>: TypeScript doesn't emit an error when assigning string | undefined to string
  • With ConfigService<Config, false>: TypeScript correctly emits a type error
  • Both should emit an error since the return type is string | undefined in both cases (due to Zod's .optional())

Minimum reproduction code

All in steps to reproduce

Steps to reproduce

// schema.ts
import { z } from 'zod';

const schema = z.object({
    KEY: z.string().optional()
});

type Config = z.infer<typeof schema>;

// service.ts
import { ConfigService } from '@nestjs/config';

class Service {
    private key: string;
    
    constructor(private config: ConfigService<Config, true>) {
        // Should error but doesn't
        this.key = this.config.get('KEY', { infer: true });
    }
}

Expected behavior

TypeScript should emit a type error in both cases (true and false) since we're trying to assign a string | undefined to a string type.

NestJS version

^11.0.1

Packages versions

        "@nestjs/common": "^11.0.1",
        "@nestjs/config": "^4.0.0",
        "@nestjs/core": "^11.0.1",
        "zod": "^4.0.0",
        "typescript": "^5.7.3",

Node.js version

22

In which operating systems have you tested?

  • [x] macOS
  • [ ] Windows
  • [x] Linux

Other

The bug seems related to how NestJS handles type inference with the WasValidated parameter in the ValidatedResult type. The true parameter appears to bypass type checking in an unexpected way.

Let me know if you need any additional information to investigate this issue.

Miguiz avatar Oct 22 '25 13:10 Miguiz

Would you like to create a PR for this issue?

kamilmysliwiec avatar Oct 22 '25 16:10 kamilmysliwiec

Me? I can try to fix it but I don't really know how to solve it.

Miguiz avatar Oct 23 '25 09:10 Miguiz

@kamilmysliwiec @Miguiz I've submitted a fix in #2213

bas0N avatar Nov 29 '25 23:11 bas0N