#Why does typescript not narrow the type down and how to fix this?

33 messages · Page 1 of 1 (latest)

grand rockBOT
#
type AnyMiddleware = (() => any) | null;

type MiddlewareContext<TMiddleware extends AnyMiddleware> =
  TMiddleware extends null
    ? { context?: never }
    : { context: Awaited<ReturnType<TMiddleware>> };
//                                  ^^^^^^^^^^^
// Type 'TMiddleware' does not satisfy the constraint '(...args: any) => any'.
//   Type 'AnyMiddleware' is not assignable to type '(...args: any) => any'.
//     Type 'null' is not assignable to type '(...args: any) => any'.
#

@misty atlas Here's a shortened URL of your playground link! You can remove the full link from your message.

perkamentus#0

Preview:```ts
type AnyMiddleware = (() => any) | null

type MiddlewareContext<
TMiddleware extends AnyMiddleware

= TMiddleware extends null
? {context?: never}
: {context: Awaited<ReturnType<TMiddleware>>}```

frank night
#

Issue is the | null

#

The smallest tweak would be to do Exclude<TMiddleware, null>

#

The first part of a conditional type works mostly like you'd expect

#

But the second part doesn't narrow the same way

#

i.e. T extends U ? /* You can treat T like U here */ : /* T won't be narrowed here, it won't be Exclude<T, U> */

#

Here's how I'd write this:

type MiddlewareOfType<T> = (() => T) | null;
type AnyMiddleware = MiddlewareOfType<any>;

type MiddlewareContext<
  TMiddleware extends AnyMiddleware
> = TMiddleware extends MiddlewareOfType<infer R>
  ? { context: Awaited<R> }
  : never;
misty atlas
frank night
#

your link shortener is not working

#

The main difference would probably be the handling of the null case. I decided to omit it entirely because I believed your intent for { context?: never } was likely that it should error in such a case. I think you may be getting an unknown you did not want

#

but I need to see an actual playground link to know what's going on

grave needle
#

(You shouldn't need a custom link shortener; just send a TS playground link as its own message and the bot will replace it)

frank night
#

If you want the same behaviour as before:

type MiddlewareOfType<T> = (() => T | Promise<T>) | null;
type AnyMiddleware = MiddlewareOfType<any>;

type MiddlewareContext<
  TMiddleware extends AnyMiddleware
> = TMiddleware extends null
  ? { context?: never }
  : TMiddleware extends MiddlewareOfType<infer R>
      ? { context: R }
      : never
misty atlas
misty atlas
grand rockBOT
#
lukeabby#0

Preview:```ts
import {z} from "zod"

export type AnyInput = z.ZodTypeAny | z.ZodNever

export type Middleware<
TInput extends AnyInput,
TOutput = any

= (params: {input: z.infer<TInput>}) => TOutput

export type AnyMiddleware = Middleware<never> | null
...```

frank night
#

You can make a reproduction shorter like this next time

#

this is with the issue btw

#

I've not attempted to solve it yet, just pare it down

misty atlas
frank night
#

The easiest fix is to AnyDef

#

Change ```ts
export type AnyDef = Def<
AnyallowedMimeTypes,
AnyFileSizeLimit,
AnyInput,
AnyMiddleware,
AnyPath

;

#

to

#
export type AnyDef = Def<
  AnyallowedMimeTypes,
  AnyFileSizeLimit,
  AnyInput,
  AnyMiddleware,
  any
>;
#

though I'd recommend using any even more in such cases

#

The issue you're running into is that AnyPath took one specific route through your conditional type

#

whereas any will instead properly take both sides of a conditional type (in most cases, you can still get it to only go through one branch)

#

and the issue was that TPath, being generic, could take either side of the conditional type whereas AnyPath is actually statically known to take a specific route

#

Avoiding any is admirable, I recommend it as much as possible, but you'll need it in cases like this where you're dealing with generic bounds that also interface with conditional types because being more concrete means it won't take both sides of the conditional type

misty atlas
#

Hmmm, Appreciate your thinking, but now I lose the path type anywhere in my app where I work with that path function... 😅

#

Are there any option with using Extract?

misty atlas
#

Got it working!