In typescript, the 'casting' operator you utilized is actually called a type assertion. The term "assertion" is used because unlike casting in other languages, it does not perform any runtime behavior such as conversion or trigger runtime errors for incompatible values. Type assertions come in handy when you possess more knowledge about the value's type than the compiler, and you wish to inform the compiler about it.
By using this assertion, you can access operations of the referrers
array that are typed with SelectItem
. So, the following code should function correctly:
class MyClass {
public referrers: SelectItemGroup[];
method() {
let s: SelectItem = this.createSelectItem();
(<SelectItem[]>this.referrers).push(s)
}
}
The array will now contain both SelectItemGroup
and SelectItem
, but it is essential to alert the compiler about this. Typescript introduces union types, which enable us to specify a type as either one of two other types (e.g., A | B
). Therefore, we can simply type the referrers
as an array of this union type without requiring the assertion:
class MyClass {
public referrers: (SelectItemGroup | SelectItem)[];
method() {
let s: SelectItem = this.createSelectItem();
this.referrers.push(s);
}
}
Upon accessing members of referrers
, a challenge arises since an element could be either SelectItemGroup
or
SelectItem</code. The compiler permits access only to common members:</p>
<pre><code>interface SelectItemGroup{
name: string;
children: SelectItem[]
}
interface SelectItem{
name: string;
value: string;
}
class MyClass {
public referrers: (SelectItemGroup | SelectItem)[];
method() {
this.referrers[0].name // valid since it's common
this.referrers[0].value // invalid as it belongs exclusively to SelectItem
this.referrers[0].children // invalid because it belongs only to SelectItemGroup
}
}
To narrow down the type of the element to one specific type, a type guard must be employed. Typescript offers various types of type guards which can be explored further here. In this scenario, utilizing an in
type guard would be most practical. This guard determines the type based on the presence of a particular field:
class MyClass {
public referrers: (SelectItemGroup | SelectItem)[];
method() {
// type-guards do not directly apply to arrays, so store the value in a local variable
let item = this.referrers[0] // item becomes SelectItemGroup | SelectItem, allowing access only to name
if('children' in item) {
item.children // valid when item is SelectItemGroup
} else {
item.value // valid when item is SelectItem (by exclusion)
}
}
}