question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Asynchronous type guards and assertion signatures

See original GitHub issue

Search Terms

async, asynchronous, promise, type guards, assertion signatures, asserts

Suggestion

TypeScript currently supports user-defined type guards and assertion signatures.

function isCustomType(value: unknown): value is CustomType {
    // ...
}

function assertIsCustomType(value: unknown): asserts value is CustomType {
    // ...
}

I think it would be great if we could also define custom asynchronous type guards and assertions.

async function isCustomType(value: unknown): Promise<value is CustomType> {
    // ...
}

async function assertIsCustomType(value: unknown): Promise<asserts value is CustomType> {
    // ...
}

Use Cases

This feature would allow to check types and validate data asnychonously. Please look at the examples below.

Examples

Imagine the code like this:

interface User {
    name: string;
}

type Valid<T> = T & {
    readonly $valid: unique symbol;
}

async function createUser(user: User): Promise<void> {
    validateUser(user);
    await saveValidUserToDatabase(user);
}

function validateUser(user: User): asserts user is Valid<User> {
    if (user.name.length < 5) {
        throw new Error('User name must be at least 5 characters long');
    }
}

async function saveValidUserToDatabase(user: Valid<User>): Promise<void> {
    // ...
}

But sometimes, validation is done asynchronously, e.g. on server-side. Currently, you can achieve it this way:

async function createUser(user: User): Promise<void> {
    await validateUser(user);
    await saveValidUserToDatabase(user as Valid<User>);
}

async function validateUser(user: User): Promise<void> {
    // ...
}

But if TypeScript supported asynchronous assertions, you could skip as Valid<User> type assertion and let the TS do the job:

async function createUser(user: User): Promise<void> {
    await validateUser(user);
    await saveValidUserToDatabase(user);
}

async function validateUser(user: User): Promise<asserts user is Valid<User>> {
    // ...
}

Exactly the same issue could be presented for user-defined type guards.

Checklist

My suggestion meets these guidelines:

  • This wouldn’t be a breaking change in existing TypeScript/JavaScript code
  • This wouldn’t change the runtime behavior of existing JavaScript code
  • This could be implemented without emitting different JS based on the types of the expressions
  • This isn’t a runtime feature (e.g. library functionality, non-ECMAScript syntax with JavaScript output, etc.)
  • This feature would agree with the rest of TypeScript’s Design Goals.

Issue Analytics

  • State:open
  • Created 3 years ago
  • Reactions:90
  • Comments:9 (2 by maintainers)

github_iconTop GitHub Comments

13reactions
zrainericommented, Aug 28, 2020

Is there any plan for this to be implemented?

I think it would be incredibly useful especially as async functions are so widely used.

The ops example is exactly how I would foresee it looking:

async function isCustomType(value: unknown): Promise<value is CustomType> {
    // ...
}

async function assertIsCustomType(value: unknown): Promise<asserts value is CustomType> {
    // ...
}

I understand you can get around it by personally asserting with “as”, however I try to avoid doing this if at all possible. I find it can lead to future issues which would have been avoided if a proper assertion function was used instead, particularly in collaborated code

P.S. thank you for all the hard work! I transitioned completely to TypeScript and haven’t looked back since

5reactions
mindplay-dkcommented, Feb 21, 2022

This proposed syntax is a bit confusing:

async function assertIsCustomType(value: unknown): Promise<asserts value is CustomType> {
    // ...
}

Where is the return-type? The return-type is boolean, but there’s no saying boolean or Promise<boolean> are the only types I might want to return after making an assertion.

A return-type declaration such as asserts value is CustomType currently implies a void return-type - again, I don’t see any practical reason why assertion functions should be limited to void return-types: an asserts declaration doesn’t describe the return-type in the first place, it describes an effect on the type of the symbol in the calling context.

How about fully separating the return-types from the assertion instead?

async function assertIsCustomType(value: unknown): Promise<boolean>, asserts value is CustomType {
    // ...
}

I think this refactors better. It also allows you to return any type you want:

async function assertIsCustomType(value: unknown): Promise<Stuff>, asserts value is CustomType {
    // ...
}

The default behavior of returning void could be explicitly written as:

async function assertIsCustomType(value: unknown): void, asserts value is CustomType {
    // ...
}

This approach would allow making more than one assertion as well:

async function assertThings(a: unknown, b: unknown): asserts a is A, asserts B is B {
    // ...
}

The way I see it, assertions functions currently don’t have a defined return-type, because asserts X is Y does not say anything about the return-type at all.

Why should the use of assertions prevent you from declaring a return-type? 🤔

Read more comments on GitHub >

github_iconTop Results From Across the Web

Asynchronous type guards - TypeScript Narrowing #7 | Academy
I'll show you the cleanest workaround for asynchronous type guards that I came ... With that in place, we can change the signature...
Read more >
Asynchronous Type Guards - TypeScript Narrowing #7
The seventh video in our TypeScript Narrowing series. In this video, I show my workaround for asynchronous type guards and assertion ...
Read more >
Type guards and assertion functions • Tackling TypeScript
TypeScript's type inference provides special support for assertion functions, if we mark such functions with assertion signatures as return types. W.r.t. how ...
Read more >
Documentation - Advanced Types - TypeScript
Type guards and type assertions. Since nullable types are implemented with a union, you need to use a type guard to get rid...
Read more >
azu on Twitter: "できないのか "Asynchronous type guards and ...
できないのか "Asynchronous type guards and assertion signatures · Issue #37681 · microsoft/TypeScript". github.com. Asynchronous type guards and assertion ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found