Using type predicates in class methods in TypeScript

Viewed 918

I have a class defined like this

class Foo {
   value: string | null;
   constructor(){
      this.value = null;
   }
   private ensureValueExists():this.value is string{ //type predicate is not legal
      this.value = "bar";
      return true;
   }
   doStuffWithValue(){
      this.ensureValueExists();
      return 5 + this.value;  //ERROR, this.value can be null
   }
} 

I would like for the ensureValueExists method to be able to tell to the compiler that this.value is indeed a string and safe to use. Is there a special syntax to use or is it no doable with TS at the moment for methods ?

2 Answers

You can use an assertion method which narrows this. Support for that isn't particularly clear from the documentation, although a commit associated with microsoft/TypeScript#32695, the PR implementing assertion functions, demonstrates that it's possible.

So in your case it would look like:

  private ensureValueExists(): asserts this is { value: string } {
    this.value = "bar";
  }

(Note that you can't return anything in an assertion function/method), and then the following works:

  doStuffWithValue() {
    this.ensureValueExists();
    return 5 + this.value;  // okay
  }
}

There are definitely caveats associated with assertion functions and methods, but since you're only operating on this, you aren't running into them here.

Playground link to code

This syntax is called a user-defined type guard. It can be used to narrow down the type of its parameter, but nothing outside of that. For example, even this code won't work:

let value: unknown

function isValueString(): value is string { // Cannot find parameter 'value'.
    return typeof value === 'string'
}

Playground link

There is also the assertion function syntax, which you probably meant to use in the first place. However, that has the same limitation as above, you can't use it on anything else than function parameters:

let value: unknown

function ensureValueIsString(): asserts value is string { // Cannot find parameter 'value'.
    value ??= 'bar'
}

Playground link

Related