I'm trying to find a way to pass an object to function in and check it type in a runtime. This is a pseudo code:
func(obj:any){
if(typeof obj === "A"){
// do something
}
else if(typeof obj === "B"{
//do something else
}
}
a:A;
b:B;
func(a);
But typeof is always return "object" and I could not find a way to get the real type of "a" or "b". The instanceof did not work either and return the same. Any idea how to do it in a TypeScript?
Thank you for your help!!!
Edit: I want to point out to people coming here from searches that this question is specifically dealing with non-class types, ie object shapes as defined by
interface
ortype
alias. For class types you can use JavaScript'sinstanceof
to determine the class an instance comes from, and TypeScript will narrow the type in the type-checker automatically.
Types are stripped away at compile-time and do not exist at runtime, so you can't check the type at runtime.
What you can do is check that the shape of an object is what you expect, and TypeScript can assert the type at compile time using a user-defined type guard that returns true (annotated return type is a "type predicate" of the form arg is T
) if the shape matches your expectation:
interface A {
foo: string;
}
interface B {
bar: number;
}
function isA(obj: any): obj is A {
return obj.foo !== undefined
}
function isB(obj: any): obj is B {
return obj.bar !== undefined
}
function func(obj: any) {
if (isA(obj)) {
// In this block 'obj' is narrowed to type 'A'
obj.foo;
}
else if (isB(obj)) {
// In this block 'obj' is narrowed to type 'B'
obj.bar;
}
}
How deep you take the type-guard implementation is really up to you, it only needs to return true or false. For example, as Carl points out in his answer, the above example only checks that expected properties are defined (following the example in the docs), not that they are they are assigned the expected type. This can get tricky with nullable types and nested objects, it's up to you to determine how detailed to make the shape check.
Expanding on Aaron's answer, I've made a transformer that generates the type guard functions at compile time. This way you don't have to manually write them.
For example:
import { is } from 'typescript-is';
interface A {
foo: string;
}
interface B {
bar: number;
}
if (is<A>(obj)) {
// obj is narrowed to type A
}
if (is<B>(obj)) {
// obj is narrowed to type B
}
You can find the project here, with instructions to use it:
I've been playing around with the answer from Aaron and think it would be better to test for typeof instead of just undefined, like this:
interface A {
foo: string;
}
interface B {
bar: number;
}
function isA(obj: any): obj is A {
return typeof obj.foo === 'string'
}
function isB(obj: any): obj is B {
return typeof obj.bar === 'number'
}
function func(obj: any) {
if (isA(obj)) {
console.log("A.foo:", obj.foo);
}
else if (isB(obj)) {
console.log("B.bar:", obj.bar);
}
else {console.log("neither A nor B")}
}
const a: A = { foo: 567 }; // notice i am giving it a number, not a string
const b: B = { bar: 123 };
func(a); // neither A nor B
func(b); // B.bar: 123
The OPs question was "I'm trying to find a way to pass an object to function in and check it type in a runtime".
Since a class instance is just an object the correct answer is to use a class instance and instanceof when runtime type checking is needed, use interface when not.
In my codebase, I will typically have a class which implements an interface and the interface is used during compilation for pre-compile time type safety, while classes are used to organize my code as well as do runtime type checks in typescript.
Works because routerEvent is an instance of NavigationStart class
if (routerEvent instanceof NavigationStart) {
this.loading = true;
}
if (routerEvent instanceof NavigationEnd ||
routerEvent instanceof NavigationCancel ||
routerEvent instanceof NavigationError) {
this.loading = false;
}
Will not work
// Must use a class not an interface
export interface IRouterEvent { ... }
// Fails
expect(IRouterEvent instanceof NavigationCancel).toBe(true);
Will not work
// Must use a class not a type
export type RouterEvent { ... }
// Fails
expect(IRouterEvent instanceof NavigationCancel).toBe(true);
As you can see by the code above, classes are used to compare the instance to the types NavigationStart|Cancel|Error.
Using instanceof on a Type or Interface is not possible since the ts compiler strips away these attributes during its compilation process and prior to being interpreted by JIT or AOT. Classes are a great way to create a type which can be used precompilation as well as during the JS runtime.
来源:https://stackoverflow.com/questions/44078205/how-to-check-the-object-type-on-runtime-in-typescript