Skip to content

Instantly share code, notes, and snippets.

@bsouthga
Last active September 16, 2018 18:08
Show Gist options
  • Save bsouthga/9941550ab2cd8c42c240f9cc1a718d61 to your computer and use it in GitHub Desktop.
Save bsouthga/9941550ab2cd8c42c240f9cc1a718d61 to your computer and use it in GitHub Desktop.
Chris's Typescript Question

Original Code in Question

link to code

High level picture

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.

My thoughts

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:

  1. make the signatures of all your renderField functions the same (and identical to the Renderer 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>` : '';
  1. Remove the Renderer annoations on renderField and check for the correct type of value in getValue before passing it to renderer.
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);
    }
}
@bsouthga
Copy link
Author

👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment