如何在 TypeScript 中检查运行时的对象类型?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/44078205/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to check the object type on runtime in TypeScript?
提问by Eden1971
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?
但是 typeof 总是返回“object”,我找不到获得“a”或“b”真实类型的方法。instanceof 也不起作用并返回相同的内容。知道如何在 TypeScript 中做到这一点吗?
Thank you for your help!!!
感谢您的帮助!!!
回答by Aaron Beall
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 classan instance comes from, and TypeScript will narrow the type in the type-checker automatically.
编辑:我想向来自搜索的人们指出,这个问题专门处理非类类型,即由
interface
或type
别名定义的对象形状。对于类类型,您可以使用 JavaScriptinstanceof
来确定实例来自哪个类,TypeScript 将自动缩小类型检查器中的类型。
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 guardthat returns true (annotated return type is a "type predicate" of the form arg is T
) if the shape matches your expectation:
您可以做的是检查对象的形状是否符合您的预期,并且 TypeScript 可以在编译时使用返回 true的用户定义类型保护来断言类型(带注释的返回类型是表单的“类型谓词” 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;
}
}
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 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.
您采用类型保护实现的深度取决于您,它只需要返回 true 或 false。例如,正如 Carl 在他的回答中指出的那样,上面的示例仅检查是否定义了预期的属性(按照文档中的示例),而不是为它们分配了预期的类型。对于可空类型和嵌套对象,这可能会变得棘手,由您决定进行形状检查的详细程度。
回答by user7132587
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.
扩展 Aaron 的答案,我制作了一个在编译时生成类型保护函数的转换器。这样您就不必手动编写它们。
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:
您可以在此处找到该项目,以及使用说明:
回答by Alpha G33k
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".
OP 的问题是“我正在尝试找到一种方法来传递一个对象来运行并在运行时检查它的类型”。
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.
由于类实例只是一个对象,因此正确的答案是在需要运行时类型检查时使用类实例和 instanceof,否则使用接口。
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
之所以有效,是因为 routerEvent 是 NavigationStart 类的实例
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.
从上面的代码可以看出,类用于将实例与 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.
在类型或接口上使用 instanceof 是不可能的,因为 ts 编译器在其编译过程中以及在被 JIT 或 AOT 解释之前剥离了这些属性。类是创建可以在预编译以及 JS 运行时使用的类型的好方法。
回答by kairun
No, You cannot reference a type
in runtime, but yesyou can convert an object
to a type
with typeof
, and do validation/sanitisation/checks against this object
in runtime.
不,你不能引用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'
}
Above is equal as
以上等于
type TypeWithAllOptionalFields = {
someKey?: string;
someKey2?: number;
};
const plainObject = {
someKey: "string",
someKey2: 1,
};
function customChecks(userInput: any) {
// ...
}
but without duplication of keynames in your code
但在您的代码中没有重复的键名
回答by Stefan Woehrer
Alternative approach without the need of checking the type
无需检查类型的替代方法
What if you want to introduce more types? Would you then extend your if-statement? How many such if-statements do you have in your codebase?
如果要引入更多类型怎么办?然后你会扩展你的 if 语句吗?你的代码库中有多少这样的 if 语句?
Using types in conditions makes your code difficult to maintain. There's lots of theory behind that, but I'll save you the hazzle. Here's what you could do instead:
在条件中使用类型会使您的代码难以维护。这背后有很多理论,但我会为你省去麻烦。您可以这样做:
Use polymorphism
使用多态
Like this:
像这样:
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
}
}
Then you just have to invoke theLogic() from whichever class you want:
然后你只需要从你想要的任何类调用 theLogic() :
let a: A = new A();
a.theLogic();
let b: B = new B();
b.theLogic();
回答by Carl Sorenson
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:
我一直在玩 Aaron 的答案,并认为测试 typeof 而不是 undefined 会更好,如下所示:
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