josh
josh

Reputation: 10318

Specify type of variable after initial assignment in TypeScript

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?

Upvotes: 1

Views: 1877

Answers (2)

Diullei
Diullei

Reputation: 12414

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

Upvotes: 1

Digvijay
Digvijay

Reputation: 784

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;
}

Upvotes: 1

Related Questions