Skip to content

User defined type guard that throws an error if arg is not of typeΒ #55066

@simondean

Description

@simondean

Suggestion

πŸ” Search Terms

  1. type guard never
  2. satisfies

βœ… Viability 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, new syntax sugar for JS, etc.)
  • This feature would agree with the rest of TypeScript's Design Goals.

⭐ Suggestion

Currently, the "User-Defined Type Guards" feature can be used to define a function where the boolean return value indicated whether or not an argument has a certain type.

This feature request is for a similar feature where a function returns true or throws an error to indicate whether or not an argument is will return if an argument is of a type or throw an error if it is not of that type. The use case is simplifying unit tests written in TypeScript.

πŸ“ƒ Motivating Example

Provide a way to take this code:

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

if (!isFish(pet)) {
  throw new Error('Pet is not a dish');
}

pet.swim();

and extract these lines into a function:

if (!isFish(pet)) {
  throw new Error('Pet is not a dish');
}

giving something like this code:

function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}

function expectFish(pet: Fish | Bird): pet satisfies Fish | never {
    if (!isFish(pet)) {
        throw new Error('Pet is not a dish');
    }

    return true;
}

expectFish(pet);
pet.swim();  // Should not generate a type error

Playground link: https://www.typescriptlang.org/play?#code/JYOwLgpgTgZghgYwgAgGLAM4AtkG8BQyRyGA7sALYAUAlAFzIBuA9sACYDc+AvvvqJFiIUAIWBQ2eQsRgAbAJ60GLdh2LIefGAFcQCMMGYhkmdNioAHCGAZmcAH2RiJ9ZFbAmMaTDgJEo1tpQxpbWyHBedjQAdGSUyACEALxJyLpsEDCgEJya+Dp6BkbIEAAeVvp2oTbe2MiOzmyu7iRwBhhZEJE+9cggEIzQUurAMMhUCaY+1TQ0w+rEYFhQzKR9EGsAolArUFQA5AAKYZh9zB5wyGw++zRc6rzS-oHByGBQ2hBcjwX6hsZlOAUCyyCDVWw9BriJrKViSPzEMoVMBVdx3J5uayxcjUdG8IA

πŸ’» Use Cases

The motivation for this feature is to avoid having to write code like this in unit tests:

if (!isFish(pet)) {
  throw new Error('Pet is not a fish');
}

expect(pet.swim()).toBeTruthy();

and instead be able to write code like this:

function expectFish(pet: Fish | Bird): pet satisfies Fish | never {
  if (!isFish(pet)) {
    throw new Error('Pet is not a fish');
  }
}

expectFish(pet);
expect(pet.swim()).toBeTruthy();

I've made up the pet satisfies Fish | never. Here's some alternative ideas for the new syntax:

  1. pet is Fish === true | never
  2. pet satisfies Fish === true | never
  3. pet is Fish ? true | never
  4. pet satisfies Fish ? true | never

Metadata

Metadata

Assignees

No one assigned

    Labels

    DuplicateAn existing issue was already created

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions