#Can't seem to infer this generic

1 messages · Page 1 of 1 (latest)

lunar grove
#
interface Foo {
  name: string;
}

function make<TModel extends Record<string, any>, TDefaultFields extends [keyof TModel] | undefined = undefined>(options?: {
  defaultFields?: TDefaultFields
}) {
  return function<TDefaultFields>(): Pick<TModel, TDefaultFields> {}
}

const fn = make<Foo>({
  defaultFields: ['foo']
})

const x = fn();

The code above is a stripped out version of what I want to reach eventually, essentially I want to create a factory function that will be able to return a subset of TModel yet I cannot event get over the first hurdle being the inference of TDefaultFields

feral stream
#

@lunar grove You can't partially infer type params.

#

When you do make<Foo> since you didn't specify the second type param, it uses its default value, undefined.

lunar grove
#

ggs, there go many hours into the drain

feral stream
#

I generally recommend trying to avoid APIs that rely on non-inferred type params, but if you need to work around it, you can split the function

#
function buildMake<TModel extends Record<string, any>>() {
  return function make<TDefaultFields extends Array<keyof TModel> | undefined = undefined>(options?: {
    defaultFields?: TDefaultFields
  }) {
    return function<TDefaultFields>(): Pick<TModel, TDefaultFields> {}
  }
}
const fn = buildMake<Foo>()({
  defaultFields: ['foo']
})
lunar grove
#

ohhh very interresting approach tbh

#

a factory factory

feral stream
#

Since type params aren't accessible at runtime, it's somewhat rare that a type-param that can't be inferred is useful.

thorn yoke
#

Yeah and realistically your buildMake<Foo>() is something you already have, since <Foo> doesn't exist at runtime it's impossible to know what model you are even using.

feral stream
#

(BTW note that [keyof TModel] is likely meant to be Array<keyof TModel>)

thorn yoke
#

(keyof TModel)[]? Or Array<keyof TModel>.

feral stream
#

ugh, super reactions

lunar grove
#

@thorn yoke There will be a fetch call in the final implementation that will use the defaultFields args to create a selection of the base model

feral stream
#

But yeah, you're going to have a hard time implementing the function you're returning from make because it can't know what TModel actual is.

lunar grove
#

i don't necesarilly hate the factory factory

feral stream
#

If you're getting the actual data from a fetch call, this is all basically just type-casting anyway.

thorn yoke
lunar grove
#

The API will always return TModel unless there a specific fields specified that you want

thorn yoke
#

Generic type parameters have to be all specified or all infered, you original design couldn't work because you want to specific TModel but infer TFields; but if your TModel is already known, then you don't need it as a generic type parameter, which leaves only TFields and that can be inferred alone.

lunar grove
#

@thorn yoke that is very important information

#

I had done something like this previously in the past but it was like you said all of them were infered

#

what would be the downside of using a class instead of an interface

#
class Foo {
  name!: string;
}

function make<TModel extends Record<string, any>, TDefaultFields extends Array<keyof TModel>>(model: TModel, options?: {
  defautlFields?: TDefaultFields
}) {
  return function<TDefaultFields>(): TModel {}
}

const fn = make(Foo, {
  defautlFields: ['name']
})

const x = fn();

something like this, it doesn't work atm but all generics are inferred

thorn yoke
#

Depends on what make actually does with the class, if you are picking/constructing it that's probably not a good idea.

lunar grove
#

Ill only use it for property names

#

in runtime ill be using for property names ^