I published a post recently about returning promises from JavaScript functions. The example code was simplified from a side project I'm working on. That project's main goal is to help me learn more TypeScript. But I'm also experimenting with using WordPress and React as a static site generator. In that project, I'm experimenting with different ways to abstract data fetching from server and client rendering as well as saving data to static files.
Returning A Promise From A JavaScript Function Is Useful
Josh Pollock ・ Jan 7 '20
In this post, I'm going to show some examples from that project that show the utility of returning promise options from JavaScript functions and how much more useful it is with TypeScript.
I am assuming that you're already familiar with TypeScript. While none of this is actually about WordPress, the data I'm fetching comes from the WordPress REST API. I wrote a post about TypeScript basics using the WordPress REST API, which might help.
Adding Types To Promises
Here is a JavaScript function from my last post about promises:
function async fetchPosts(page = 1){
// Get posts
const posts = await fetch( `https://site.com/wp-json/wp/v2/posts?page=${page}` )
//Then parse
.then( r => r.json() );
// Then return object
return posts;
}
This returns an array of posts. That's cool, but the IDE and the babel compiler don't know that. I, as someone using this function in another file, may not know what it returns. TypeScript helps make it really explicit what that type of promise is being returned.
That example was simplified from this function in my project:
const fetchPosts = async (
endpoint: string,
page: number = 1,
postType: string = "posts"
): Promise<Array<WpApiPost>> => {
return fetch(`${endpoint}/wp/v2/${postType}?page=${page}`).then(r =>
r.json()
);
};
This one is a little more complete. It uses arguments for page and post type, as well as the URL for the API. In my actual code, I added a line with //@ts-ignore
inside the closure as Jest was raising a TypeScript error that the URL was not absolute, even though the string literal does create a valid, absolute URL. Please comment if you know a better solution, I'm at 🤷.
The key part of this is the return type Promise<Array<WpApiPost>>
. This type has three parts. The first part Promise
tells us the at the function returns a promise, which TypeScript's compiler would have figured out on it's own. The second part tells us that this promise resolves an array. The third part shows us this array is a collection of objects of the WpApiPost
.
Why This Is Useful
Now I can think of this function, when I'm consuming it as a black-box. I know what goes in and what comes out and I don't care what is inside... as long as its tests pass.
Consuming this function is now a lot simpler. In strict mode, I have to tell the callback for this promise what to expect. For example, in a React component:
//Type the posts variable and argument for setPosts to a collection of WpApiPost
const [posts, setPosts ] = React.useState<Array<WpApiPost>>();
//Query for posts
React.useEffect( () => {
//fetch posts
fetchPosts( props.endpoint, props.page, props.postType )
// Type return of promise to match fetchPosts and setPosts common types.
.then( ( r : <Array<WpApiPost>> ) => setPosts(r) );
}, [ props.endpoint, props.page, props.postType, setPosts ] );
This shows how to add TypeScript typings to React.setState()
. We do a similar typing with array.map, when iterating over this array with React, when rendering:
{posts.map( (post: WpApiPost) => <Post key={post.id} post={post} /> )}
I Promise This Is Super Useful
In this post, I rewrote one of the examples from my last dev.to post but showed how returning a promise from a function is even more useful with TypeScript. No surprise, TypeScript is great.
Encapsulating API fetch logic inside of a function is helpful, but it can make it harder to work with the results. Using TypeScript makes it clear what the resulting data will be, and how to use it, when the promise is resolved.
Featured Image: Photo by David Marcu on Unsplash.
Top comments (4)
There's also the shorthand of
instead of
Good point. I like that it reads in order it unpacks:
But, shorter is generally better. 🤷♀️
Nah, I think it's just preference really, neither is better
If you have worked with DefinitelyTyped, which offers linting, you will find that bracketed favors Array, while otherwise favors [].