无法从联合类型访问参数(TypeScript)(Unable to access parameters from union types (TypeScript))

为什么无法访问联合类型中的属性,如下所示:

export interface ICondition { field: string operator: string value: string } export interface IConditionGroup { conditions: ICondition[] group_operator: string } function foo(item: ICondition | IConditionGroup) { if(typeof item.conditions === "undefined") { // does not work let field = item.field; // does not work ///.. do something } else { let conditions = item.conditions; // does not work /// .. do something else } }

我收到这些错误:

error TS2339: Property 'conditions' does not exist on type 'ICondition | IConditionGroup'. error TS2339: Property 'conditions' does not exist on type 'ICondition | IConditionGroup'. error TS2339: Property 'field' does not exist on type 'ICondition | IConditionGroup'.

但我必须使用类型来使其工作 - 像这样:

function foo2(inputItem: ICondition | IConditionGroup) { if(typeof (<IConditionGroup>inputItem).conditions === "undefined") { let item= (<ICondition>inputItem); let field = item.field; ///.. do something } else { let item= (<IConditionGroup>inputItem); let conditions = item.conditions; /// .. do something else } }

我知道JS中丢失了类型信息,为什么我必须在TS中明确地转换它?

Why am not able to access attributes in union types like this:

export interface ICondition { field: string operator: string value: string } export interface IConditionGroup { conditions: ICondition[] group_operator: string } function foo(item: ICondition | IConditionGroup) { if(typeof item.conditions === "undefined") { // does not work let field = item.field; // does not work ///.. do something } else { let conditions = item.conditions; // does not work /// .. do something else } }

I get these errors:

error TS2339: Property 'conditions' does not exist on type 'ICondition | IConditionGroup'. error TS2339: Property 'conditions' does not exist on type 'ICondition | IConditionGroup'. error TS2339: Property 'field' does not exist on type 'ICondition | IConditionGroup'.

But I have to cast types to get it to work - like this:

function foo2(inputItem: ICondition | IConditionGroup) { if(typeof (<IConditionGroup>inputItem).conditions === "undefined") { let item= (<ICondition>inputItem); let field = item.field; ///.. do something } else { let item= (<IConditionGroup>inputItem); let conditions = item.conditions; /// .. do something else } }

I understand that the type information is lost in JS, so why do I have to explicitly cast it in TS?

最满意答案

Typescript使用Type Guards处理它,通常它很简单:

if (typeof item === "string") { ... } else { ... }

要么

if (item instanceof MyClass) { ... } else { ... }

但在你的情况下,因为你使用的是不可能的接口,所以你需要创建自己的用户定义类型防护 :

function isConditionGroup(item: ICondition | IConditionGroup): item is IConditionGroup { return (item as IConditionGroup).conditions !== undefined; } function foo(item: ICondition | IConditionGroup) { if (isConditionGroup(item)) { let conditions = item.conditions; // do something } else { let field = item.field; // do something else } }

( 游乐场代码 )

你也可以在没有护卫的情况下做到这一点:

function foo(item: ICondition | IConditionGroup) { if ((item as IConditionGroup).conditions !== undefined) { let conditions = (item as IConditionGroup).conditions; // do something } else { let field = (item as ICondition).field; // do something else } }

但这是详细的方式,因为你需要键入断言item 3次而不是一次。

Typescript handles this with Type Guards, usually it's as simple as:

if (typeof item === "string") { ... } else { ... }

Or

if (item instanceof MyClass) { ... } else { ... }

But in your case as you're using interfaces that's not possible, so you'll need to create your own User-Defined Type Guards:

function isConditionGroup(item: ICondition | IConditionGroup): item is IConditionGroup { return (item as IConditionGroup).conditions !== undefined; } function foo(item: ICondition | IConditionGroup) { if (isConditionGroup(item)) { let conditions = item.conditions; // do something } else { let field = item.field; // do something else } }

(code in playground)

You can also do it without type guards:

function foo(item: ICondition | IConditionGroup) { if ((item as IConditionGroup).conditions !== undefined) { let conditions = (item as IConditionGroup).conditions; // do something } else { let field = (item as ICondition).field; // do something else } }

But that's way to verbose as you need to type assert item 3 times instead of once.

无法从联合类型访问参数(TypeScript)(Unable to access parameters from union types (TypeScript))

为什么无法访问联合类型中的属性,如下所示:

export interface ICondition { field: string operator: string value: string } export interface IConditionGroup { conditions: ICondition[] group_operator: string } function foo(item: ICondition | IConditionGroup) { if(typeof item.conditions === "undefined") { // does not work let field = item.field; // does not work ///.. do something } else { let conditions = item.conditions; // does not work /// .. do something else } }

我收到这些错误:

error TS2339: Property 'conditions' does not exist on type 'ICondition | IConditionGroup'. error TS2339: Property 'conditions' does not exist on type 'ICondition | IConditionGroup'. error TS2339: Property 'field' does not exist on type 'ICondition | IConditionGroup'.

但我必须使用类型来使其工作 - 像这样:

function foo2(inputItem: ICondition | IConditionGroup) { if(typeof (<IConditionGroup>inputItem).conditions === "undefined") { let item= (<ICondition>inputItem); let field = item.field; ///.. do something } else { let item= (<IConditionGroup>inputItem); let conditions = item.conditions; /// .. do something else } }

我知道JS中丢失了类型信息,为什么我必须在TS中明确地转换它?

Why am not able to access attributes in union types like this:

export interface ICondition { field: string operator: string value: string } export interface IConditionGroup { conditions: ICondition[] group_operator: string } function foo(item: ICondition | IConditionGroup) { if(typeof item.conditions === "undefined") { // does not work let field = item.field; // does not work ///.. do something } else { let conditions = item.conditions; // does not work /// .. do something else } }

I get these errors:

error TS2339: Property 'conditions' does not exist on type 'ICondition | IConditionGroup'. error TS2339: Property 'conditions' does not exist on type 'ICondition | IConditionGroup'. error TS2339: Property 'field' does not exist on type 'ICondition | IConditionGroup'.

But I have to cast types to get it to work - like this:

function foo2(inputItem: ICondition | IConditionGroup) { if(typeof (<IConditionGroup>inputItem).conditions === "undefined") { let item= (<ICondition>inputItem); let field = item.field; ///.. do something } else { let item= (<IConditionGroup>inputItem); let conditions = item.conditions; /// .. do something else } }

I understand that the type information is lost in JS, so why do I have to explicitly cast it in TS?

最满意答案

Typescript使用Type Guards处理它,通常它很简单:

if (typeof item === "string") { ... } else { ... }

要么

if (item instanceof MyClass) { ... } else { ... }

但在你的情况下,因为你使用的是不可能的接口,所以你需要创建自己的用户定义类型防护 :

function isConditionGroup(item: ICondition | IConditionGroup): item is IConditionGroup { return (item as IConditionGroup).conditions !== undefined; } function foo(item: ICondition | IConditionGroup) { if (isConditionGroup(item)) { let conditions = item.conditions; // do something } else { let field = item.field; // do something else } }

( 游乐场代码 )

你也可以在没有护卫的情况下做到这一点:

function foo(item: ICondition | IConditionGroup) { if ((item as IConditionGroup).conditions !== undefined) { let conditions = (item as IConditionGroup).conditions; // do something } else { let field = (item as ICondition).field; // do something else } }

但这是详细的方式,因为你需要键入断言item 3次而不是一次。

Typescript handles this with Type Guards, usually it's as simple as:

if (typeof item === "string") { ... } else { ... }

Or

if (item instanceof MyClass) { ... } else { ... }

But in your case as you're using interfaces that's not possible, so you'll need to create your own User-Defined Type Guards:

function isConditionGroup(item: ICondition | IConditionGroup): item is IConditionGroup { return (item as IConditionGroup).conditions !== undefined; } function foo(item: ICondition | IConditionGroup) { if (isConditionGroup(item)) { let conditions = item.conditions; // do something } else { let field = item.field; // do something else } }

(code in playground)

You can also do it without type guards:

function foo(item: ICondition | IConditionGroup) { if ((item as IConditionGroup).conditions !== undefined) { let conditions = (item as IConditionGroup).conditions; // do something } else { let field = (item as ICondition).field; // do something else } }

But that's way to verbose as you need to type assert item 3 times instead of once.