Update: the question as stated doesn't indicate that the request is to be able to distinguish the type without checking properties. Unfortunately, that is impossible; TypeScript compiles to JavaScript, which is what actually runs. And TypeScript's type system, where interfaces like A
and B
are found, is completely erased when the code is compiled. So there is no A
or B
at runtime to check.
If you want to be able to distinguish, at runtime, whether myObj
is A
or B
, you need to write JavaScript code that does this... such as checking properties. What you can do is use TypeScript to help you write this code and give it typings so that the compiler also understands that your property check narrows an A | B
to an A
or a B
. And that's what the rest of this answer (and the other question's answer) tells you. Good luck!
The other question's answer suggests that you write a user-defined type guard function, which will definitely work; the idea is that you tell the compiler what you consider to be a good way to distinguish between A
and B
, and it will let you use that function to do so.
An easier way to do it in your case is to use the in
operator, which can be used as a type guard (since TypeScript 2.7). For example:
declare const obj: A | B;
if ("b_id" in obj) {
// obj is a B in here
console.log(obj.name.toUpperCase());
} else {
// obj is an A in here
console.log(obj.a_id.toUpperCase());
}
Note that this is technically unsafe, since it's possible to have an object of type A | B
which isn't distinguishable that way:
function getEvilAorB(): A | B {
interface EvilA extends A {
b_id: number;
}
const evilA: EvilA = { name: "", a_id: "", b_id: 123 };
return evilA; // okay, EvilA extends A extends A | B
}
Notice that getEvilAorB()
returns a valid A
, which is therefore a valid A | B
, but unfortunately it will fail the above test because of the numeric name
property:
const evilA = getEvilAorB();
if ("b_id" in evilA) {
console.log(evilA.b_id.toUpperCase()); // compiles but at runtime
// TypeError! evilA.name.toUpperCase is not a function
}
If you aren't concerned about such edge cases, then the in
type guard is probably sufficient. Otherwise you should consider either writing a user-defined type guard, or making your interfaces members of a discriminated union which is a conventional and supported way to have the compiler distinguish between union members.
Hope that helps; good luck!
Playground link to code