1

I have an interface Base, which interfaces A and B extend, and A and B both have properties that are unique and don't exist in the other. In my code, I have some data that will change depending on the type:

interface Base {
  basicProperty: any,
  basicProperty2: any
}

interface A extends Base {
  aSpecificProperty: any
}

interface B extends Base {
  bSpecificProperty: any
}

function(type): A | B {
  const data: A | B = {
    basicProperty: 1,
    basicProperty2: 2
  }

  if (type === 'A') {
    // Data should be type A
    data.aSpecificProperty = 3;
  } else if (type === 'B') {
    // Data should be type B
    data.bSpecificProperty = 3;
  }

  return data;
}

Is there any way to accomplish this without TypeScript complaining about the type not containing the right properties? Or, is there a way to reassign/specify the type of data from A | B to A or B?

2 Answers 2

1

Have you considered using the typeof operator?

interface IBase {
    name: string;
    sayName();
    }


class A implements IBase {
    constructor(public name: string) {  }
    sayName() {
        console.log(name);
    }

    onlyA()
    { 
        console.log("I exist only in A");
    }
}

class B implements IBase {
    constructor(public name: string) {  }
    sayName() {
        console.log(name);
    }

    onlyB()
    { 
        console.log("I exist only in B");
    }
}

function myFunc<T extends IBase>(arg: T ) : any {
    
    const data: T = arg;

    if (typeof(data) === typeof(A)) {
        // Data should be type A
        data.sayName();
        let aObj: A = data as any;
        aObj.onlyA();
    } else if (typeof(data) === typeof(B)) {
        // Data should be type B
        data.sayName();
        let bObj: B = data as any;
        bObj.onlyB();
    }

    return data;
}

let a = myFunc(new A("I am A"));
let b = myFunc(new B("I am B"));

a.onlyA();
b.onlyB();

This essentially let the generic function check the type and return an object of type any.

Is this what you were looking for or i did not follow the question right?

Per your example it would look something like

interface IBase{

}

class A implements IBase { 

}

class B implements IBase{

}

function myFunc<T extends IBase>(type: T ): A | B {
    const data: A | B = {

    }

    if (typeof(type) === typeof(A)) {
        // Data should be type A
    } else if (typeof(type) === typeof(B)) {
        // Data should be type B
    }

    return data;
}

Sign up to request clarification or add additional context in comments.

Comments

1

There are many ways to achieve what you want. If your return is based on type: string argument, why not use something like:

interface Base { }
class A implements Base { name: string }
class B implements Base { value: number }

function fn(type): Base {
  if (type == 'A') {
    const data: A = {
      name: 'abc'
    }
    //...
    return data;
  }

  if (type == 'B') {
    const data: B = {
      value: 10
    }
    //...
    return data;
  }
}

I need more information about your problem to give a better answer, but, since you have a Base class as you said, something like this could help you.

UPDATE

Based on your updates and comments I'll suggest the following code:

interface Base {
  basicProperty: any,
  basicProperty2: any
}

interface A extends Base {
  aSpecificProperty: any
}

interface B extends Base {
  bSpecificProperty: any
}

function typeFactory<T extends Base>(obj: Base, ext: Partial<T>) {
  return Object.assign(obj, ext) as T;
}

function myFn(type): A | B {
  const data: Base = {
    basicProperty: 1,
    basicProperty2: 2
  }

  switch (type) {
    case 'A':
      return typeFactory<A>(data, {
        aSpecificProperty: 3
      });
    case 'B':
      return typeFactory<B>(data, {
        bSpecificProperty: 3
      });
  }
  // ...
}

reference about Object.assign(...) https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign

4 Comments

I updated the question to make it a little bit more specific about my use case.
Ok, I got it. Let me ask you, about create the data object inside the if con block as I suggested, is it an option to you? Otherwise you will need to declare the data as your Base interface (data: Base) and you will need to use cast operation to change the type inside the if condition.
I simplified the data in the example above, but the data is a large object... so it would be a lot of duplication if I copied the data in each. I actually figured out separately what to do by looking at your suggestion to cast (I create the "base data" with no type, then create vars for A and B with their respective types) and assign in the if blocks. Does that make sense?
If it is your scenario it make sense. Based on your updates and comments I updated my answer with a new code snippet.

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.