14

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?

1
  • I would say the way you did it looks fine. However the question itself is primarily opinion based and I'll vote to close it on that basis. :) Commented Mar 1, 2016 at 15:53

2 Answers 2

14

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;
}
Sign up to request clarification or add additional context in comments.

5 Comments

Why can't you check if i.Name exists and differentiate the types based on that?
@jocull What if the object was an instance of NamedItem, but its Name was undefined?
If it's missing non-optional data, is it really an instance of NamedItem then?
@jocull I suppose that depends on your use case.
are there any downsides of using classes over interfaces? This is probably the best solution I found around differentiating types in typescript.
8

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
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.