2019-02-10
Being explicit with your own React Hook + TypeScript return type
blogentry, programming, react, typescript
blogentry, programming, react, typescript
_Photo by Rafaela Biazi on _Unsplash
TheAifam5 graciously reported an issue for react-use-localstorage that even though it was written using TypeScript, it wasn't distributing TypeScript type declaration file.
react-use-localstorage is a React Hook that returns an array of Local Storage item and a way to set it
Initially written in JavaScript but recently converted to TypeScript to learn TypeScript.
I followed TheAifam5's instruction to generate a type file and distributed it.
But when I tried to use the new distrubution within a React + TypeScript project, I was getting the following error.
[ts] Cannot invoke an expression whose type lacks a call signature. | |
Type 'string | ((item: string) => void)' has no compatible call signatures. [2349] |
Error Message
Error Message in VS Code
Below is the full source code for useLocalStorage.
import React from "react"; | |
export default function useLocalStorage(key: string, initialValue: string = "") { | |
const [item, setValue] = React.useState(() => { | |
const value = localStorage.getItem(key) || initialValue; | |
localStorage.setItem(key, value); | |
return value; | |
}); | |
const setItem = (item: string) => { | |
setValue(item); | |
window.localStorage.setItem(key, item); | |
}; | |
return [item, setItem]; | |
} |
useLocalStorage - react-use-localstorage on NPM
As you can see, useLocalStorage returns an array of [item, setItem].
When auto-generating a type file using tsc, TypeScript generates following definition code.
export default function useLocalStorage(key: string, initialValue?: string): (string | ((item: string) => void))[]; | |
//# sourceMappingURL=index.d.ts.map |
Bad return type
TypeScript inferred the return type as (string | ((item: string) => void))[] which is not right.
So to fix it you need to explicitly declare the return type of useLocalStorage to generate a correct type definition.
export default function useLocalStorage(key: string, initialValue: string = "") | |
: [string, (item: string) => void] { | |
// rest omitted for brevity ... | |
} |
Return type specified explicitly
You can now see that TypeScript has generated the definition correctly.
export default function useLocalStorage(key: string, initialValue?: string): [string, (item: string) => void]; | |
//# sourceMappingURL=index.d.ts.map |
Good return type
And TypeScript is happy (in VS Code).
TypeScript is now 🙂
Does anyone know why TypeScript wasn't able to infer the return type correctly?
UPDATE: 2019-02-12
pgrizzay & AngularBeginner have generously answered the question above in Reddit.
The gist is that, TypeScript does not infer tuple type variable because there isn't enough information.