如何在TypeScript中检查运行时的对象类型?

时间:2017-05-19 20:02:32

标签: typescript types runtime detect typeof

我正在尝试找到一种方法来传递一个对象来运行并在运行时检查它的类型。这是一个伪代码:

func(obj:any){
  if(typeof obj === "A"){
    // do something
  }
  else if(typeof obj === "B"{
    //do something else
  }

}
 a:A;
 b:B;
 func(a);

但是typeof总是返回“对象”,我找不到获得真正类型“a”或“b”的方法。 instanceof也不起作用并返回相同的内容。 知道如何在TypeScript中做到这一点吗?

感谢您的帮助!!!

7 个答案:

答案 0 :(得分:26)

  

编辑:我想指出那些来自搜索的人,这个问题是专门处理非类型的,即对象   由interfacetype别名定义的形状。对于班级类型你   可以使用JavaScript来自instanceof to determine the class的实例,而TypeScript会自动缩小类型检查器中的类型。

类型在编译时被删除,并且在运行时不存在,因此您无法在运行时检查类型。

您可以做的是检查对象的形状是否符合预期,TypeScript可以使用返回true的user-defined type guard在编译时断言类型(带注释的返回类型为"类型)形式arg is T)的谓词"如果形状符合您的期望:

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;
  }
}

Example in Playground

你采取类型保护实现的深度取决于你,它只需要返回true或false。例如,正如Carl在他的回答中指出的那样,上面的例子只检查是否定义了预期的属性(遵循文档中的示例),而不是它们被分配了预期的类型。使用可空类型和嵌套对象可能会变得棘手,您可以自行决定如何详细检查形状。

答案 1 :(得分:4)

扩展了Aaron的答案,我制作了一个在编译时生成类型保护功能的转换器。这样,您不必手动编写它们。

例如:

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
}

您可以在此处找到该项目以及使用说明:

https://github.com/woutervh-/typescript-is

答案 2 :(得分:0)

我一直在研究Aaron的答案,并认为最好测试typeof而不是仅仅进行不确定的测试,像这样:

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

答案 3 :(得分:0)

,您无法在运行时引用type,但,您可以使用以下命令将object转换为type typeof,然后在运行时对此object进行验证/清除/检查。

const plainObject = {
  someKey: "string",
  someKey2: 1,
};
type TypeWithAllOptionalFields = Partial<typeof plainObject>; //do further utility typings as you please, Partial being one of examples.

function customChecks(userInput: any) {
  // do whatever you want with the 'plainObject'
}

以上等于

type TypeWithAllOptionalFields = {
  someKey?: string;
  someKey2?: number;
};
const plainObject = {
  someKey: "string",
  someKey2: 1,
};
function customChecks(userInput: any) {
  // ...
}

但在代码中不重复键名

答案 4 :(得分:0)

无需检查类型的替代方法

如果要引入更多类型怎么办?然后,您是否可以扩展if语句?您的代码库中有多少个此类if语句?

在条件中使用类型会使代码难以维护。这背后有很多理论,但我会避免麻烦。您可以改用以下方法:

使用多态性

赞:

abstract class BaseClass {
    abstract theLogic();
}

class A extends BaseClass {
    theLogic() {
       // do something if class is A
    }
}


class B extends BaseClass {
    theLogic() {
       // do something if class is B
    }
}

然后,您只需要从所需的任何类中调用theLogic():

let a: A = new A();
a.theLogic();

let b: B = new B();
b.theLogic();

答案 5 :(得分:0)

您可以调用构造函数并获取其名称

let className = this.constructor.name

答案 6 :(得分:-1)

OPs的问题是“我试图找到一种方法来传递对象以起作用并在运行时检查其类型”。

由于类实例只是一个对象,正确的答案是在需要运行时类型检查时使用类实例和instanceof,在不需要时使用接口。

在我的代码库中,我通常将有一个实现接口的类,该接口在编译期间用于确保编译前时间类型的安全,而类则用于组织我的代码以及在打字稿中进行运行时类型检查。

之所以可行,是因为routerEvent是NavigationStart类的实例

if (routerEvent instanceof NavigationStart) {
  this.loading = true;
}

if (routerEvent instanceof NavigationEnd ||
  routerEvent instanceof NavigationCancel ||
  routerEvent instanceof NavigationError) {
  this.loading = false;
}

将无法正常工作

// Must use a class not an interface
export interface IRouterEvent { ... }
// Fails
expect(IRouterEvent instanceof NavigationCancel).toBe(true); 

将无法正常工作

// Must use a class not a type
export type RouterEvent { ... }
// Fails
expect(IRouterEvent instanceof NavigationCancel).toBe(true); 

从上面的代码可以看到,类用于将实例与NavigationStart | Cancel | Error类型进行比较。

无法在类型或接口上使用instanceof,因为ts编译器在其编译过程中以及在被JIT或AOT解释之前会剥离这些属性。类是创建可在JS运行时使用的预编译类型的好方法。