Reputation: 23515
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;
}
}
How can I say to typescript,
starting now, this variable is of type "String"
Upvotes: 0
Views: 349
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
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