#Confused with generics

86 messages · Page 1 of 1 (latest)

earnest cedarBOT
#
piscopancer#0

Preview:```ts
type TQueryOption<O, F extends keyof O = keyof O> = {
field: F | "name"
operator?: "=="
value: O[F][] | O[F]
}

type TUser = {name: string}
type TSurvey = {questions: string[]}

type TCollection =
| {
path: "users"
type: TUser
}
| {path: "surveys"; type: TSurvey}
...```

slender basalt
#

you can see the error popping up in the playground. I also simplified the below functions for you

small kraken
#

your (TCollection & {path: P})['type'] types are wrong

#

it's not doing what you think it's doing

#

if you want the part of the union with the path attribute being P, use Extract<TCollection, { path: P }>

#

@slender basalt

earnest cedarBOT
#
ascor8522#0

Preview:```ts
type TQueryOption<O, F extends keyof O = keyof O> = {
field: F | "name"
operator?: "=="
value: O[F][] | O[F]
}

type TUser = {name: string}
type TSurvey = {questions: string[]}

type TCollection =
| {path: "users"; type: TUser}
| {path: "surveys"; type: TSurvey}
...```

slender basalt
#

😮

slender basalt
#

@small kraken your example is working fine but I still need <O extends ...> in below functions to be of type type in TCollection. In the example you suggested, they are of type {path: ..., type: ...}

#

Generally, O must be either TUser or TSurvey

small kraken
small kraken
slender basalt
#

damn that's going to be hard I need to show the whole chunk of code

#

in this file I applied your suggestion

#

when I use queryDocs

const surveys = await queryDocs('surveys', [{field: 'community', value: true}])

the error is
Type '"community"' is not assignable to type '"__name__" | "path" | "type"'.ts(2322)

#

you see community field is part of TSurvey

#

I am using firebase, you must have recognized it

slender basalt
#

@small kraken 🥺

#

what the hell does he want...

small kraken
#

idk, try making a minima reproduction

#

also, firebase is unusable inside of the playground
the types don't load

barren sigil
#

It's a typo mistale @slender basalt

#

mistake

#
export function getCollection<P extends TCollection['path']>(path: P) {
#

uh wait

#

I am a mistake

#

I was like in any case you'll end up with a correspondence problem issue there and it does look like that is the case

#
function queryDoc<Extract<{
    path: "users";
    type: "u";
}, {
    path: P;
}> | Extract<{
    path: "surveys";
    type: "s";
}, {
    path: P;
}>, P>(path: P, options: string | TQueryOption<Extract<{
    path: "users";
    type: "u";
}, {
    ...;
}> | Extract<...>, keyof Extract<...> & keyof Extract<...>>[]): Promise<...>
slender basalt
#

what do I do then?

#

ascor's solution using Extract worked, however <O> needs to be either TUser or TSurvey

slender basalt
barren sigil
#

The weird Extracts is because it can't eager resolve P but that's not that big of an issue

#

The intersection in the second argument to TQueryOption however is classic correspondence problem

slender basalt
#

typescript team must have fixed it, what's the problem?

#

it is either hard to fix or what

barren sigil
#

fixed it? I'm still breaking my head over it so I sure hope someone would have told me then 😅

slender basalt
#

must have*

#

they in fact did not fix it

#

unluckily to us

#

I tried asking BAI to fix this chunk of code, it did not know what to do though

barren sigil
#

I'm kind of confused why you have this:

export type TQueryOption<O, F extends keyof O = keyof O> = {
    field: F | '__name__'
    operator?: WhereFilterOp
    value: O[F][] | O[F]
}
#

this keyof O will always give 'path' | 'type'

slender basalt
#

actually no

barren sigil
#

Did you mean to use keyof O['type']?

slender basalt
#

if we feed it an object, then it will burp out its keys

barren sigil
#

Oh so it

#

It's supposed to receive the type itself in O?

#

Not the TCollection item ?

slender basalt
#
export type TUser = {
    provider: 'google' | 'github' | 'email'
    photoURL?: string
    uId: string
    username: string
    likedSurveys?: string[]
    createdSurveys?: string[]
    subscriptions?: string[]
    subscribers?: string[]
}
barren sigil
#

because currently it's just being sent one of the TCollection's

slender basalt
#

In the original playground, TDoc consumes TUser or TSurvey

#

that's how I was working with this type all the time

barren sigil
#

Yeah this is classic correspondence problem

slender basalt
#

😢

barren sigil
#

no use trying to fix it, it is unsolvable

#

!*:correspondence-problem

earnest cedarBOT
#
retsam19#0
`!retsam19:correspondence-problem`:

There's a particular pattern that is safe but hard for the Typescript compiler to handle, which I call the "correspondence problem":

const functionsWithArguments = [
  { func: (arg: string) => {}, arg: "foo" },
  { func: (arg: number) => {}, arg: 0 },
];

for (const { func, arg } of functionsWithArguments) {
  func(arg);
//     ^^^
// Argument of type 'string | number' is not assignable to parameter of type 'never'.
//   Type 'string' is not assignable to type 'never'.
}

The problem is that func is typed as (x: string) => void | (x: number) => void and arg is string | number, but the compiler can't prove that they "correspond": that, for example, arg is only a string when func accepts strings.

As far as the type are concerned, arg could be number, and func could be (arg: string) => void, and that would be a type-error. It's easy for us to see that that won't happen, but that requires understanding the program at a higher-level than the level the compiler operates.

Depending on the specifics there's sometimes clever fixes, but usually I recommend using a type assertion and ignoring the issue:

func(arg as never);
slender basalt
#

yes, now I am using this

#

I do not want headaches

slender basalt
barren sigil
#

What's missing is "the type of a union or intersection access".

#

Currently TS derives string | number for the code (string[] | number[])[0]

#

this is not correct

#

I've been working on this in a branch for a few months but it's a complex problem.

slender basalt
#

hell man we are wasting our life in an attempt of fixing bugs brought by other developers in our code

barren sigil
#

haha

#

Life of a developer

slender basalt
#

of a ts chad

barren sigil
#

At least we can see the code these days

slender basalt
#

?

barren sigil
#

(As opposed to before all these things were open source and you could only guess that the issue was in the thing you are using instead of in your own code)

slender basalt
#

this is frightening

#

also, why do you not have github linked to your discord? I wanted to see it

#

do you overconcern your projects privacy? 😂

barren sigil
#

Oh no just never thought of adding it

#

sec

#

voila

#

I add comments here when I find out new things:

#

(that's the ticket tracking "oneof constraints" but it turns out this request actually reduces to the same limitation)

#

(note my early posts were a lot of Ritalin induced ramblings when I didn't know much about the issue yet. it's a lot more clear by now)

#

I've started over like 3 times by now, every time realizing that what I'm doing is actually just a special case of a more fundamental issue

#

Implementing "the type of a union/intersection access" or what I call "existential unions/intersections" in those writeups is related to a lot of things:

  • Correspondence problem
  • Functions that can only be parameterized with 1 of a union of types
  • Could replace distributive unions
  • Could largely replace mapped types
  • Could fix a lot of type level programming issues
  • ...probably more