Orelsanpls
Orelsanpls

Reputation: 23515

Typescript variable type specification

I have a case where a variable could be type A or type B. Then using a If I know what type the variable is and I apply an appropriate response.

I got TSLINT errors.

function checkIfItsAString(foo: String | Number): boolean {
  return foo instanceof String;
}

function example2(foo: String | Number) {
  if (checkIfItsAString(foo)) {
    foo.charAt(5);

    return;
  }
}

enter image description here

enter image description here

How can I say to typescript,

starting now, this variable is of type "String"

Upvotes: 0

Views: 349

Answers (2)

VLAZ
VLAZ

Reputation: 29007

You can use a type predicate to have the compiler automatically narrow down the types. Your predicate already works, to make it a type predicate you just have to say what it implies about the value:

function checkIfItsAString(foo: String | Number): foo is String {
//  tell the compiler true means it's a string -> ^^^^^^^^^^^^^
  return foo instanceof String;
}

This lets the compiler determine the type automatically:

function example2(foo: String | Number) {
  if (checkIfItsAString(foo)) { //foo is a String

    foo.charAt(5);

    return;
  }

  foo.toFixed(""); //foo is a Number
}

Live demo on TypeScript Playground

Alternatively, you can use the in operator directly which will do type elimination from a list of types:

function example2(foo: String | Number) {
  if ("charAt" in foo) {
  //   ^^^^^^ this only exists on String not on Number, so it's a String
    foo.charAt(5);

    return;
  }

  foo.toFixed(2); //a number, since it doesn't have "charAt"
}

Live demo on TypeScript Playground

This is more useful for one-off simpler checks, so you don't need an entire predicate to handle it. It can be used to narrow down types, if that is a use case. Here is a contrived example that uses in to eliminate types in several steps.

/* 
 * ake a Nnumber, String, array of Numbers, or the HTMLCollection array-like:
 * for Number - the value
 * for HTMLCollection - the length
 * for array of number - the sum + the length
 * for string - the length + the trimmed length
 */
function dummyExample(x : Number | String | Number[] | HTMLCollection) : number {
  if ("length" in x) { // String | Number[] | HTMLCollection
    let totalLength: number = x.length;

    if ("slice" in x) { // String | Number[]
      if ("reduce" in x) { // Number[]
        return x.reduce((a: number, b: Number) => a + b.valueOf(), 0) + length;
      } else { // String
        return x.trim().length + totalLength;
      }
    } else { // HTMLCollection
      return totalLength;
    }
  } else { // Number
    return x.valueOf();
  }
}

Live demo on TypeScript Playground

Upvotes: 1

jonrsharpe
jonrsharpe

Reputation: 122024

The compiler can't infer anything useful from the boolean return type of checkIfItsAString; the name of that function tells other human readers of the code what you expect, but that doesn't help the compiler at all.

Instead, you need to be explicit that this is a type predicate, i.e. inform the compiler that it can use this to narrow the type of a variable based on the result. Instead of just boolean, it is going to return whether foo is String:

function checkIfItsAString(foo: String | Number): foo is String {

The actual implementation doesn't need to change, this is just extra information for the compiler.

Upvotes: 1

Related Questions