Skip to content

Type-defined, fat-arrow function type guards do not compile #14826

Closed
@chriskrycho

Description

@chriskrycho

Currently, the behavior of type guards is asymmetric between function declarations and arrow-function declarations.

TypeScript Version: 2.2.1

Code

This works:

type A = { a: boolean };
type B = { b: string };

type ToCheck = A | B | A & B;

function hasA(arg: ToCheck): arg is A | A & B {
  return arg.hasOwnProperty('a');
}

function hasB(arg: ToCheck): arg is B | A & B {
  return arg.hasOwnProperty('b');
}

Expected behavior:

So should this:

type A = { a: boolean };
type B = { b: string };

type ToCheck = A | B | A & B;

type HasA = (arg: ToCheck) => arg is A | A & B;
const hasA: HasA = arg => arg.hasOwnProperty('a');

type HasB = (arg: ToCheck) => arg is B | A & B;
const hasB: HasB = (arg) => arg.hasOwnProperty('b');

Actual behavior:

The compiler reports:

Type '(arg: ToCheck) => boolean' is not assignable to type 'HasA'. Signature '(arg: ToCheck): boolean' must have a type predicate.

This is an unfortunate asymmetry. I prefer to use the type/const bindings throughout for various reasons, not least because it's extremely convenient for a type-driven design approach where I write the types out ahead of time and populate the fat-arrow function bindings later. E.g. in the case which motivated this, I did just that: I wrote out the equivalent of the HasA and HasB type definitions (along with a bunch of others), then followed up by writing the hasA and hasB bodies.

The anonymity of the function is not at issue; given either of the above definitions, this works just fine:

const hasBoth = function(arg: ToCheck): arg is A & B {
  return hasA(arg) && hasB(arg);
}

Unsurprisingly, the same limitation exist with interface types—this does not work, either:

interface HasBoth { (arg: ToCheck): arg is A & B }
const hasBoth: HasBoth = (arg) => hasA(arg) && hasB(arg);

It would be great if type guards could be generalized to work with the type-definition and interface forms.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions