type ComponProps = {
mode?: 'mode1' | 'mode2'
}
function Compon(props: ComponProps) {
//
}
export type FunctionOrValue<T, Args extends any[] = any[]> = T | ((...args: Args) => T)
type FieldOption<Compon extends (...args: any[]) => any> = {
element?: Compon
props?: FunctionOrValue<Parameters<Compon>[0], [any]>
}
const createField = <Compon extends (...args: any[]) => any>(opt: FieldOption<Compon>) => opt
let field = createField({
element: Compon,
/** this will be ok */
// props: {
// mode: 'mode1',
// },
/** but this shows error, why? */
/** add "as const" after "'mode1'" literal
* will be ok but not ideal */
props: () => {
return {
mode: 'mode1',
}
},
/** What Error says: */
/**
* Type '() => { mode: string; }' is not assignable to type 'FunctionOrValue<ComponProps, [any]> | undefined'.
* Type '() => { mode: string; }' is not assignable to type '(args_0: any) => ComponProps'.
* Call signature return types '{ mode: string; }' and 'ComponProps' are incompatible.
* The types of 'mode' are incompatible between these types.
* Type 'string' is not assignable to type '"mode1" | "mode2" | undefined'.ts(2322)
* ground.ts(95, 3): The expected type comes from property 'props' which is declared here on type 'FieldOption<(props: ComponProps) => void>'
*/
})
#why is this not assignable?
38 messages · Page 1 of 1 (latest)
does turning:
13 | props?: FunctionOrValue<Parameters<Compon>[0], [any]>
into:
13 | props?: FunctionOrValue<ComponProps>
Solve your problem?
or however you need it, just remove the Parameters
the object you're returning is being inferred to { mode: string }, as seen in your error.
use as const on the string or the object to get it to infer narrower.
/** add "as const" after "'mode1'" literal
- will be ok but not ideal */
why wouldn't it be ideal
it's literally what as const is for
idk, its whats written in the question
oh, i didn't notice it.
no clue what's up with that then, as const is a proper fix here
its the double parameter expansion
Parameter is overkill and spreading too much
removing it from the FieldOption type fixes it
type FieldOption<Compon extends (...args: any[]) => any> = {
element?: Compon
props?: FunctionOrValue<Compon, [any]>
}
Parameters is the key
this is not the same thing
to build relation between the element and props
Parameters isn't the issue here
but then it's not generic?
what?
im confused now
the error is talking about the return type, not the parameter list
AH
can you get the idea? the Scenario
when you configure a form field, createField({ element: ... props: ... }), props will automatically be the component's props' type
yeah, i see now
and ideally , i dont want developers who use this hav to write 'as const'
this isn't really about your implementation, it's about how the returned object is structured
the inference herer is not quite reasonable
you say it needs to be { mode?: 'mode1' | 'mode2' }, but the value { mode: 'mode1' } doesn't automatically preserve the type of 'mode1', it's broadened to string
that's how literals in ts work
if you had mode: string or mode: SomeObject it'd be fine
ok, so why no error when i just write props object instead of function
because there the object never gets broadened, it's immediately checked against the type
then why return object not immediately checked?
there's nothing to immediately check against
it'd check against the return type of the function, but you didn't specify it, so it infers like a normal object
you could explictly write the return type of the function as well
Preview:ts ... props: (): ComponProps => { return { mode: "mode1", } }, ...
i see. thank you man