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
staticIsAnErroractually a type predicate? Give a minimal reproducible example.x as ManuallyCastTypebut I'd suggest using a type predicatecheckIfItsAString(foo: String | Number): foo is Stringthis way you don't have to manually tell the compiler every time by casting the value