Skip to content

fix: auth typings and reduced type ambiguity#4562

Merged
Marsup merged 2 commits into
hapijs:masterfrom
damusix:fix/auth-types-2
Feb 18, 2026
Merged

fix: auth typings and reduced type ambiguity#4562
Marsup merged 2 commits into
hapijs:masterfrom
damusix:fix/auth-types-2

Conversation

@damusix
Copy link
Copy Markdown
Contributor

@damusix damusix commented Feb 13, 2026

@Marsup Marsup added this to the 21.4.5 milestone Feb 18, 2026
@Marsup Marsup self-assigned this Feb 18, 2026
@Marsup Marsup added the types TypeScript type definitions label Feb 18, 2026
@Marsup Marsup merged commit 0ead0ec into hapijs:master Feb 18, 2026
10 checks passed
@fjeldstad
Copy link
Copy Markdown

This should have been part of a major version bump as the type changes are breaking in some scenarios. For example when extracting a header value; that used to be string | undefined while now it is string | string[] | undefined meaning that it cannot be assigned to a string variable anymore. Even if it is more correct now, I don't think consumers expect the build to break from installing a patch version bump.

@Marsup
Copy link
Copy Markdown
Contributor

Marsup commented Mar 6, 2026

@fjeldstad we already answered that in #4563. Breaking types is inherent to the typesrcipt ecosystem, nobody releases a major version just for types correctness.

@kanongil
Copy link
Copy Markdown
Contributor

kanongil commented Mar 6, 2026

nobody releases a major version just for types correctness

FYI, some projects do try to do that by eg. adhering to the proposed semver-ts spec. However, even with that, changes in minor releases can cause type compilation issues! As they state (paraphrased):

We propose the rules below, with the caveat that they will not prevent all possible breakage—only the majority of it.

As such, for typescript development, one must expect that any typings update can potentially cause "breakage".

Copy link
Copy Markdown
Contributor

@kanongil kanongil left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fjeldstad argued that the narrowed types should have been in a breaking release. As already discussed, breakage is somewhat expected.

I will now argue that the types should never have been narrowed, as evident from my inline comments. Maybe undo this mess?

Comment thread lib/types/request.d.ts

export interface RequestQuery {
[key: string]: any;
[key: string]: string | string[] | undefined;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is going on here? This is a bad choice. After validation the value can be anything. Parsing to a number or a Date are valid choices. This should probably just be unknown.

Copy link
Copy Markdown
Contributor Author

@damusix damusix May 9, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The thing is: If you don't validate, it will most certainly be string | string[] | undefined. This is the actual possibility of runtime data types Hapi would produce.

If you do validate, it's whatever you've set with Joi, which is a custom definition, and depends on you not using schema.strict(), which would make string | string[] | undefined true again. At that point you might as well include a custom ServerRoute if you're depending on the types to be correct:

ServerRoute<{
  Headers: { 
    'x-my-custom-header': number 
    'x-date': Date
  }
}>

unknown | unknown[] | undefined would imply: reality depends on the user's Joi usage and Joi configuration for ALL routes— Joi is the source of runtime truth. This seems like the wrong assumption.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand your point and it definitely has merits, but ultimately I think it is flawed.

Header, query, and param validation and transformation is core to using Hapi, so it doesn't make sense to have a default type that only safely applies when it is not used. It does not have to be Joi, but some kind of validation and possible transformation is very much expected.

Whether the default should be unknown or any can be debated, but given Hapi tends to prefer safety and strictness I would probably go with unknown, so any parameter usage would have to be explicit.

Btw, for query params the types are even more uncertain, since it also depends on the parser server option. With eg. the suggested qs module it can also contain plain objects, which is now impossible to type, as it is not a subset of string | string[] | undefined.

declare module "@hapi/hapi" {
    interface ReqRefDefaults {
        Query: {
            [key: string]: string | string[] | Record<string, object> | undefined;
        };
    }
}

…fails with this error:

node_modules/@hapi/hapi/lib/types/request.d.ts:298:18 - error TS2430: Interface 'ReqRefDefaults' incorrectly extends interface 'InternalRequestDefaults'.
  Types of property 'Query' are incompatible.
    Type '{ [key: string]: string | string[] | Record<string, object> | undefined; }' is not assignable to type 'RequestQuery'.
      'string' index signatures are incompatible.
        Type 'string | string[] | Record<string, object> | undefined' is not assignable to type 'string | string[] | undefined'.
          Type 'Record<string, object>' is not assignable to type 'string | string[] | undefined'.
            Type 'Record<string, object>' is missing the following properties from type 'string[]': length, pop, push, concat, and 29 more.

Comment thread lib/types/request.d.ts
Payload: stream.Readable | Buffer | string | object;
Query: RequestQuery;
Params: Record<string, any>;
Params: Record<string, string>;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, the actual type is the result of the validation step.

Comment thread lib/types/request.d.ts
Params: Record<string, string>;
Pres: Record<string, any>;
Headers: Record<string, any>;
Headers: Record<string, string | string[] | undefined>;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, the actual type is the result of the validation step.

Comment thread typescript.md
Comment on lines +72 to +73

This is the interface you augment via `declare module` to change defaults globally. Any key you add here overrides `InternalRequestDefaults` for all routes that don't provide their own refs.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With the current narrow typing, it is impossible to change eg. the Query default to another type. It can only be further narrowed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

types TypeScript type definitions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants