What's the proper way to handle a situation where you have two interfaces that are similar enough that you want to run them through the same piece of logic:
interface DescriptionItem {
Description: string;
Code: string;
}
interface NamedItem {
Name: string;
Code: string;
}
function MyLogic(i: DescriptionItem | NamedItem) {
var desc = (<DescriptionItem>i).Description || (<NamedItem>i).Name;
return i.Code + ' - ' + desc;
}
This works; however, my question is on improving the var desc = ...
line. Is what I have above the best option? Or is there a better way to handle this situation in Typescript?
In TypeScript, we can define a variable which can have multiple types of values. In other words, TypeScript can combine one or two different types of data (i.e., number, string, etc.) in a single type, which is called a union type. Union types are a powerful way to express a variable with multiple types.
We can differentiate between types in a union with a type guard. A type guard is a conditional check that allows us to differentiate between types. And in this case, a type guard lets us figure out exactly which type we have within the union.
TypeScript Interface TypeTypeScript allows you to specifically type an object using an interface that can be reused by multiple objects. To create an interface, use the interface keyword followed by the interface name and the typed object.
// That said, we recommend you use interfaces over type aliases. Specifically, because you will get better error messages. If you hover over the following errors, you can see how TypeScript can provide terser and more focused messages when working with interfaces like Chicken.
I know this question is very old, but I was playing around with the same problem, as I was learning the difference between | and & when making Type Unions.
there are some options to solve this problem (that is also linter-friendly). The best way is to use a discriminator in all your interfaces (a narrow interface).
//First create a super-interface that have the discriminator
interface B
{
kind:'b1'|'b2' //define discriminator with all the posible values as string-literals (this is where the magic is)
}
interface B1 extends B
{
kind: 'b1' //now narrow the inherited interfaces literals down to a single
//after that add your interface specific fields
data1: string;
data: string;
}
interface B2 extends B
{
kind:'b2' //now narrow the inherited interfaces literals down to a single
//after that add your interface specific fields
data2: string;
data: string;
}
//lets initialize 1 B1 type by using the literal value of a B1 interface 'b1'
var b1: B1|B2 = {
kind:'b1',
data: 'Hello From Data',
data1:'Hello From Data1'
//typescript will not allow you to set data2 as this present a B1 interface
}
//and a B2 Type with the kind 'b2'
var b2: B1|B2 = {
kind: 'b2',
data: 'Hello From Data',
//typescript will not allow you to set data1 as this present a B2 interface
data2: 'Hello From Data2'
}
another option is to check for fields on an object using the "in"-keyword, but this can cause a lot of boilerplate code, as you have to update it every time you change your interface.
interface B1
{
data1: string;
data: string;
}
interface B2
{
data2: string;
data: string;
}
var b3: B1|B2 = {
data: 'Hello From Data',
data1:'Hello From Data1',
data2:'Hello From Data2'
}
console.log(b3.data); //this field is common in both interfaces and does not need a check
if('data1' in b3) //check if 'data1' is on object
{
console.log(b3.data1);
}
if('data2' in b3){
console.log(b3.data2); //check if 'data2' is on object
}
TypeScript interfaces
only exist at compile-time, so there isn't much you can do to test for interface types at run-time. The code you specified in your question makes sense and is probably your best option.
However, if you have the flexibility to change your interfaces
to classes
, you can use TypeScript's type guards to do more elegant type checking:
class DescriptionItem {
Description: string;
Code: string;
}
class NamedItem {
Name: string;
Code: string;
}
function MyLogic(i: DescriptionItem | NamedItem) {
let desc: string;
if (i instanceof DescriptionItem) {
desc = i.Description;
} else {
desc = i.Name;
}
return i.Code + ' - ' + desc;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With