#complex overload signatures
117 messages · Page 1 of 1 (latest)
FYI i have a meeting in 10 mins, but will take a quick look now
absolutely no problem
weird, the playground isn't highlighting the problematic call site for me. is that happening for you too?
i only see a red squggly at the function definition
same
but i'm used to getting weird errors by now
i'm probably doing something wrong but don't know any better
i think you're referring to the server2 call. do you expect that call to match the first overload signature? or no?
nah you can ignore server2 since it's a more complex version of server1
i think the issue is actually the overload itself
okay then i'm getting confused by the error message then. which call is failing? server1 or client1 or both?
yeah i guess you're right
yeah i'm trying to figure out what i'm doing wrong, it seems to get screwy when i add undefined
oh duh sorry, i was primed by the error message you mentioned in #ts-discussion and didn't even read this one. this is a different error
yeah don't worry, i think it's all related
i tweaked something and that one disappeared
it's all to do with something in the overloads being off
so the gist of this one is that the options type you define in each overload must be a subtype of the options type in the implementation signature. ditto for the return types
i haven't yet figured out which specific thing is violating that
seems like the return types?
i am getting it back to basics atm
maybe more than just that, actually
i think you'll at least need to make server and client optional in the implementation signature
ok so i have it back at a basic thing that 'works' to go from
so here's where it starts getting messy
L 14
sorry but i gotta run now. feel free to keep posting and i'll catch up later
thanks
z.infer<z.ZodObject<ST>>
if i want to combine ST & CT - since i don't want CT to be a default of ZodRawShape, i need to add | undefined afaik
this seems to work
so, if i just build up until i encounter an error you'll have something to answer
now i want to introduce HasKeyPrefix
what's the best route forward here
anything i attempt seems to not work, so i guess this is the blocker as things stand
as far as i can tell the implementation at the bottom of the overloads seems ok here btw?
i'm back now
is the | undefined on CT important to you? it's not the cause of the overload error, just checking whether i can ignore it
the undefined isn't important, no, but i need a way to somehow type it properly so ST & CT infer correctly
seems like the main issue is that the result of applying HasKeyPrefix is not always assignable to z.ZodRawShape (which is how schema is typed in the implementation signature). the 'bad key no prefix' error result is at least part of the root cause of that (because that string literal type is not assignable to all of the possible properties of z.ZodRawShape, i guess)
i think i'd just drop the cute error message and instead reduce to never in the bad cases:
Preview:```ts
...
type HasKeyPrefix<
T extends z.ZodRawShape,
P extends string
= {
[K in keyof T]: K extends${P}${string}
? T[K]
: never
}
...```
i'm happy with never, iirc i changed the type as i was trying to debug an earlier error and just hadn't changed it back
so now there is a new error on L13 in your above playground
the error there is simple enough, but it has a knock-on effect that i am unsure of how to deal with properly
what's the appropriate way to write the type when undefined has been thrown into the mix?
i'm starting to think an extra overload might be the way to go?
something like this, but it doesn't quite work
The call would have succeeded against this implementation, but implementation signatures of overloads are not externally visible.
check it out, that message is back
i believe this means the overload is not catching this particular variation, and it is saying the implementation would work, but it's not usable
yeah, that's exactly what it's saying
i genuinely have no idea how i'm supposed to proceed with this lol
separating into another overload sounds like a simple fix, but TS isn't able to narrow it seems
sorry but there's kinda just too much going on there for me to easily see what you're trying to do, so i'm going to need a little help. let's take the errors one at a time. for the server2 call, which of the overload signatures do you want it to match?
the second one is my guess?
yes. here's one i reduced a bit already:
Preview:```ts
import {z} from "zod"
type RuntimeValues = string
type MatchedRuntime<T> = {
[K in keyof T]: RuntimeValues
}
type HasKeyPrefix<
T extends z.ZodRawShape,
P extends string
= {
[K in keyof T]: K extends${P}${string}
? T[K]
: never
...```
yeah ok let's work from your reduced one
L38 should match L23 in the second overload?
are you saying that's what you're trying to make happen?
btw how come you removed the base implementation for the overloads
can you do that in general?
it doesn't matter
you're getting type errors, so we can ignore the runtime stuff
these are also declare functions now
just trying to stay focused and make things easier to see at a glance
yeah no problem, i didn't know you could do that
i think you're saying you want this to typecheck?
Preview:```ts
import {z} from "zod"
type RuntimeValues = string
type MatchedRuntime<T> = {
[K in keyof T]: RuntimeValues
}
type HasKeyPrefix<
T extends z.ZodRawShape,
P extends string
= {
[K in keyof T]: K extends${P}${string}
? T[K]
: never
...```
that's just the second overload on its own (and therefore no longer an overload)
and in that simpler example you can see the error more clearly. it seems your conditional type isn't behaving the way you expect
yeah, i think your analysis is on point
i don't see anything wrong with that type though
since it looks like CT is getting inferred as z.ZodRawShape (which you can see by hovering over the call), it reduces to this:
Preview:```ts
import {z} from "zod"
type HasKeyPrefix<
T extends z.ZodRawShape,
P extends string
= {
[K in keyof T]: K extends${P}${string}
? T[K]
: never
}
const x: HasKeyPrefix<z.ZodRawShape, "PRE_"> = {
PRE_A: z.string(),
PRE_B: z.boolean(),
}
...```
i suspect it's that inference which is the problem? like you don't expect the above to typecheck, yeah?
i.e. you want CT to be inferred as something more specific than z.ZodRawShape?
yeah, if ZodRawShape reaches the function then it takes a supertype and messes it up
cool, so let's pop back to this one and see if we can figure out why CT is being inferred so widely
in your latest tspg i'd expect that to work?! i still don't see the problem with the type
oh wait you do?
no properties in in z.ZodRawShape will extend PRE_${string}, right?
i guess i should actually look at that type, 1 sec
{ PRE_A: z.string(), PRE_B: z.boolean() }
that is a valid shape, it's a Record<string, ZodTypeAny>
sure, but it's not a valid HasKeyPrefix<z.ZodRawShape, 'PRE_'>
look at what that type computes to:
(Record<string, never>)
i am not understanding why it is defaulting to never
HasKeyPrefix is mapping over the keys of z.ZodRawShape, which is just string. string does not extend `PRE_${string}`, so the result of that conditional type is never
they're all never
yeah, because none of them have any keys that start with 'PRE_'
here's one that isn't never:
Preview:```ts
import {z} from "zod"
type HasKeyPrefix<T, P extends string> = {
[K in keyof T]: K extends ${P}${string}
? T[K]
: never
}
type Example3 = HasKeyPrefix<
{PRE_foo: "whatever"},
"PRE_"
// ^?```
oh i see...
if you just want to focus on working through these specific type errors that's fine (type tetris can be kinda fun), but in case it'd be more useful to take a step back: at the end of the day what are you trying to do here? why do you need to prove that the zod validators that people are passing in only have specific prefixes on their keys?
if you don't want to take a step back, maybe you can describe in english what HasKeyPrefix on its own is supposed to do? its name doesn't seem to match its definition from my understanding of it, and maybe that's part of the problem
so checking string always ends up in never
meaning only literals (inferred values) are going to not return never
if literal is not available, the supertype is selected, and ends up as never
the type is supposed to ensure all keys start with that prefix
it'll be fine as long as i can guarantee the inference is passed in i think
i'd write that more like this:
type HasKeyPrefix<T, P extends string> = keyof T extends `${P}${string}` ? T : never
is it not being a mapped type a functionally different idea?
i'm not sure i understand the question, but that type does have different behavior from the mapped version. here's an example:
type HasKeyPrefix1<T, P extends string> = {
[K in keyof T]: K extends `${P}${string}`
? T[K]
: never
}
type HasKeyPrefix2<T, P extends string> = keyof T extends `${P}${string}` ? T : never
type Example1 = HasKeyPrefix1<{ PRE_foo: 'a', notPRE_bar: 'b' }, 'PRE_'>
// ^? - type Example1 = {
// PRE_foo: 'a';
// notPRE_bar: never;
// }
type Example2 = HasKeyPrefix2<{ PRE_foo: 'a', notPRE_bar: 'b' }, 'PRE_'>
// ^? - type Example2 = never
wow, i was going to say i doubt that's enough to solve the server2 problem, but i was wrong. this works!
Preview:```ts
import {z} from "zod"
type RuntimeValues = string
type MatchedRuntime<T> = {
[K in keyof T]: RuntimeValues
}
type HasKeyPrefix<
T,
P extends string
= keyof T extends
${P}${string}? T : never
declare function createEnv<
ST extends z.ZodRawSha
...```
i guess the different shape allows for better inference
back in 5, i have to reset, things are crashing
oof
actually, looking all the way back here, does this change solve the remaining issues? looking again i suspect client1 is supposed to be a type error (because it uses the wrong prefixes)?
Preview:```ts
import {z} from "zod"
type RuntimeValues = string
type MatchedRuntime<T> = {
[K in keyof T]: RuntimeValues
}
type HasKeyPrefix<
T,
P extends string
= keyof T extends
${P}${string}? T : never
function createEnv<ST extends z.ZodRawShape>(op
...```
i think i should go to sleep now actually, got an early start tomorrow. hopefully that helps. cheers!