DEV Community

Cover image for Returning A Promise From A Function Is Extra Useful With TypeScript
Josh Pollock
Josh Pollock

Posted on • Updated on

Returning A Promise From A Function Is Extra Useful With TypeScript

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.

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;
}
Enter fullscreen mode Exit fullscreen mode

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()
    );
};
Enter fullscreen mode Exit fullscreen mode

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 ] );
Enter fullscreen mode Exit fullscreen mode

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} /> )}
Enter fullscreen mode Exit fullscreen mode

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.

Thanks for reading, follow me on Twitter @Josh412

Featured Image: Photo by David Marcu on Unsplash.

Discussion (4)

Collapse
georgewl profile image
George WL • Edited on

There's also the shorthand of

<WpApiPost[]>

instead of

<Array<WpApiPost>>
Collapse
shelob9 profile image
Josh Pollock Author • Edited on

Good point. I like that it reads in order it unpacks:

// An array of WpApiPost
<Array<WpApiPost>>

// A promise that resolves to an array of WpApiPost objects.
<Promise<Array<WpApiPost>>>

But, shorter is generally better. 🤷‍♀️

Collapse
georgewl profile image
George WL

Nah, I think it's just preference really, neither is better

Thread Thread
patarapolw profile image
Pacharapol Withayasakpunt

If you have worked with DefinitelyTyped, which offers linting, you will find that bracketed favors Array, while otherwise favors [].