#Why is `x` `Builder<unknown>` instead of `Builder<{value: number}>`?

1 messages · Page 1 of 1 (latest)

burnt marshBOT
#
alexcubed#0

Preview:```ts
class Builder<RET = unknown> {
public define<NAME extends string, RET>(name: NAME) {
return <ARGS>(getter: (args: ARGS) => RET) => {
const self = this as any as Builder<RET>

  return self
}

}
}

var x = new Builder().define("test")<{
hi: boolean
n: number
}>(() => ({value: 42}))```

sacred cove
#

RET can't be inferred from the define call

#

and NAME probably doesn't need to exist there. you aren't using it

sweet geode
sweet geode
sacred cove
#

RET isn't used in the parameter list of define

#

new Builder().define("test") what type is this

sacred cove
sweet geode
sacred cove
#

exactly

#

(well, it's a (getter: (args: ARGS) => unknown) => Builder<unknown>)

sweet geode
#

oh right

sacred cove
#

but nothing about that call says { value: number }

sacred cove
#

how so

sweet geode
#

it's actually the name of a socket event, so i need its actual value so that i can have type interference on my socket.on(...) thing

#

why you're asking?

sacred cove
#

ah, ok. makes sense, thought this was isolated

sacred cove
# sweet geode why you're asking?

overusing generics is a common beginner pitfall; i can't tell your expertise level from behind the screen, so better safe than sorry.

sweet geode
#

ah yea fair :)

#

im writing a library so it def feels like im overusing generics xD

sacred cove
#

anyways, if you want RET to be inferred from calling the returned function, move it there

sweet geode
#

the problem is that i want the user of the define function to manually specify the type of ARGS

#

without having to specify RET nor NAME

#

actually i might've found a good enough workaround

sacred cove
#

since you're already using currying, that's a pretty straightforward workaround for the "no partial inference" thing

sweet geode
#

wdym?

#

it's not working tho?

sacred cove
#

add another layer, each layer for a generic lol

#

NAME is inferred, ARGS is specified, RET is inferred, so 3 sets, 3 functions

sweet geode
#

ooh

sacred cove
#

right now you have 2, define() and the inner curry

sweet geode
#

i see

#

hadn't thought of that

sacred cove
#

if you're going that route, at least. im not sure currying will be very popular with your users lol

sweet geode
#

well for now im my only user haha

#

but yea that user doesn't really like currying

#

i'll do it differently

#

thanks for the help!

sacred cove
#

how exactly are you using ARGS?

sweet geode
#

bts getter is actually another builder

#

where ARGS is the type of the initial data for that builder

#

so more like <INITIAL_DATA>(operation: (operationBuilder: OperationBuilder<INITIAL_DATA>) => OPERATION)

sacred cove
#

since Builder is taking RET which is inferred, why not just have ARGS be inferred as well?

#

would reduce duplication and simplify the signature here

sweet geode
#

builder.build() returns RET

sacred cove
#

for its own build?

#

or for this one as well

#

like, is this taking a callback that infers RET or not

sweet geode
sacred cove
sweet geode
#

no OperationBuilder is a different builder than Builder

sacred cove
#

so how does it define its own InitialData?

sweet geode
#

It's whatever the main Builder's current DATA is

sacred cove
#

that sounds awfully fragile

sweet geode
#

no it's fine :P

#

i think

sacred cove
#

type parameters for functions should always be inferable from the parameter list, unless it's just initializing an empty structure, as per:

#

!:thats-no*

burnt marshBOT
#
tjjfvi#0
`!tjjfvi:thats-no-generic-its-a-type-cast`:

When you use a type parameter in the function, it should generally be inferrable from the arguments.

For example, this is a misuse of generics:

function getMeA<T>(): T {
   /* magic */
}

because getMeA<string>() and getMeA<number>() compile to the same code at runtime, there's so way to implement this function safely (other than always throwing); this is just a type cast in disguise. Instead of using a generic here, you should return unknown, and cast at the call site if necessary, to be clear it's an unsafe operation:

-function getMeA<T>(): T {
+function getMeA(): unknown {
    /* magic */
 }

-getMeA<number>()
+getMeA() as number

One exception to this rule is if you're returning a possibly-empty container of T. For example, these are all perfectly safe, even though the generic can't be inferred from the parameters:

function emptyArray<T>(): Array<T> {
  return []
}

function useRef<T>(): { current?: T } {
  return {}
}
sacred cove
#

that's just kinda a general rule for most if not all languages with generics

sweet geode
#

i think im not violating that

#

but my brain is kinda slow atm

#

the whole ctx is a bit more complicated than the breakdown i showed in the playground

sacred cove
#

you kinda are with ARGS if you're saying it shouldn't be inferred

sweet geode
#

in my case its the same as doing new List<ARGS> pretty much i think

sacred cove
#

mm doesn't really sound like it? builders don't typically function as containers

sweet geode
#

it's a builder for building a definition of an operation, the ARGS is like the data that someone needs to give as input when creating an operation instance based on the operation definition

sacred cove
#

that's certainly a unique take on a builder

#

sounds more like a consumer

sweet geode
#

do you have a link to a definition for that?

#

im not too familiar with the pattern names so i might indeed be using the wrong names

sacred cove
#

i'm using the java term for consumer

#

yeah

sweet geode
#

i found this but that's definitely different

sacred cove
#

eh, the consumer part of that pattern

#

just, something that accepts a certain type of input and does work on it

sweet geode
#

unless that description is not clear enough

sacred cove
#

the builder pattern doesn't typically have

data that someone needs to give as input when creating an operation

sacred cove
sweet geode
sacred cove
#

no, ignore that part lol

sweet geode
#

what part exactly?

sacred cove
sacred cove
#

"producer" is a separate thing, "consumer" is a separate thing, using them together for concurrent work is the "producer-consumer pattern"

sweet geode
#

ahh

#

okay now i get that part xd

sweet geode
#

wait no

#

im confusing myself

sacred cove
#

i think both of your builders are creating consumers instead of concrete structures

#

builders are generally for putting together a structure, piece by piece, where some parts may be optional and defaulted
if there's some variability to the structure, it'd generally be inferable from the args you give it

sweet geode
#

and i feel like also the subbuilder

sacred cove
sweet geode
sacred cove
#

how would you use ARGS

sweet geode
#

operationDefinition.create(initialData: ARGS): Operation

#

the 'subbuilder' builds an OperationDefinition

sacred cove
#

...so you're building a builder?

#

that's... convoluted, but i see how it all lines up now

sweet geode
#

an operationDefinition is not a builder

sacred cove
#

a factory, then

#

(if im remember factories correctly...)

sweet geode
#

i think so?

sweet geode
#

that's why i called it operationdefinition and not operationfactory 😝

#

but maybe it is a factory indeed

sacred cove
#

yeah i think it's a builder creating a factory creating objects

#

which.. why do you need that extra layer?

#

if you return a modified version of a builder instead of mutating it, you could reuse them

sweet geode
#

the factory layer?

sacred cove
#

yeah

#

a factory is just a deferred constructor

sweet geode
#

then it's definitely a factory

#

I need that(=factorylayer) because other places might need to create objects using that factory

sacred cove
#

...wait, what does your build method look like

#

its signature i mean

sweet geode
#

for the main builder?

sacred cove
#

yeah

#

well, both

sweet geode
#

they're still wip

sacred cove
#

are they zero-arg?

sweet geode
#

yes

sacred cove
sweet geode
#

no

#

that wouldnt work

sacred cove
#

or put it on the builder as another method or similar

sacred cove
sweet geode
sweet geode
#

the underlying reason for that requires a much more convoluted answer im afraid

sacred cove
#

eh, not really

sweet geode
#

now i can just say that the thing in the define() method should return an OperationDefinition rather than an OperationBuilder that only has a build method