Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript Union Types: Dealing with Interfaces

Tags:

typescript

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?

like image 839
John Avatar asked Mar 01 '16 15:03

John


People also ask

What are union types 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.

Can I check a type against a union type in TypeScript?

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.

Are interfaces types in TypeScript?

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.

Should I use interface or type in TypeScript?

// 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.


2 Answers

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
}
like image 67
Droa Avatar answered Sep 28 '22 10:09

Droa


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;
}
like image 36
Nathan Friend Avatar answered Sep 28 '22 12:09

Nathan Friend