I think you ran into a subtle typescript typing bug that was fixed by the introduction of the strictFunctionTypes
compiler option. We didn't turn on this on while I was there as there was a lot of stuff to fix if we did.
I think the issue is that you are declaring your renderers to be of type Renderer
, which allows any type matching FieldValue
as an argument.
Specifically:
// `renderField1: Renderer` means that `renderField1` is considered to be
// of type `Renderer` when used elsewhere. The type declaration of `(value: string) => ...`
// only matters within the context of the function, as the declaration takes precidence.
const renderField1: Renderer = (value: string) =>
`<div>${value}</div>`
This type of annotation is actually an error, but (I think) wasn't caught until Typescript added the strictFunctionTypes
compiler option.
Try turning it on in the options to see the error. The issue is that you really shouldn't be able to assing a function
of type (value: string) => string
to a variable of type (value: string | boolean) => string
, as the former accepts
a more limited type of argument.
I see two solutions:
- make the signatures of all your
renderField
functions the same (and identical to theRenderer
type). For example:
// the type of `value` is inferred by the `Renderer` annotation.
const renderField1: Renderer = value =>
// now that renderField is a generic `Renderer`, we need to check to make sure we have the right value.
typeof value === 'string' ? `<div>${value}</div>` : '';
const renderField2: Renderer = value =>
typeof value === 'boolean' ? (value ? `<div>YES</div>` : `<div>NO</div>`) : '';
const renderField3: Renderer = value =>
typeof value === 'object' ? `<div>${value.whatever}${value.somethingElse}</div>` : '';
- Remove the
Renderer
annoations onrenderField
and check for the correct type ofvalue
ingetValue
before passing it torenderer
.
const renderField1 = (value: string) =>
`<div>${value}</div>`
const renderField2 = (value: boolean) =>
value ? `<div>YES</div>` : `<div>NO</div>`
const renderField3 = (value: ArbitrarilyComplexType) =>
`<div>${value.whatever}${value.somethingElse}</div>`
function getValue(id: FieldId, value: FieldValue) {
switch (true) {
case id === FieldId.FIELD_1 && typeof value === 'string': return renderField1(value);
case id === FieldId.FIELD_2 && typeof value === 'boolean': return renderField2(value);
case id === FieldId.FIELD_3 && typeof value === 'object': return renderField3(value);
}
}
👍