Michael Gee
Michael Gee

Reputation: 35

How to properly change typescript type based on conditional logic

Hey guys I was wondering if there was a correct way to do this. I am currently trying to define a type of variable based on conditional logic but am currently running into issues. Both Interface1 and Interface2 are defined as interfaces in Typescript. With the first console.log of the type of the eventProps variable I get an undefined which I thought I would originally log out the type of: 'Interface1 | Interface2'. And with the second console.log I log out is of type object which I thought would log out either 'Interface1' or 'Interface2'. Am I doing something wrong/is there a better way to do what I am trying to accomplish?

let eventProps: Interface1 | Interface2;
console.log(typeof eventProps);

switch (event) {
  case 'event1':
    eventProps = { isCorrect: true, name: 'Mike' } as Interface1;
  default:
    eventProps = { isCorrect: true } as Interface2;
}
console.log(typeof eventProps);

Upvotes: 0

Views: 2087

Answers (1)

parktomatomi
parktomatomi

Reputation: 4079

When you write Typescript code, it is compiled to Javascript and the type annotations and checking are wiped away.

typeof is a Javascript operator that only reports what primitive type the object is, so it's working as intended.

If you're using a named class or prototype, console.log will include the type name. And you can use instanceof to test it at runtime:

class ConcreteType1 implements Interface1 {
  isCorrect: boolean;
  name: string;

  constructor(isCorrect: boolean, name: string) {
    this.isCorrect = isCorrect;
    this.name = name;
  }
}

const x:ConcreteType1 = getAConcreteType1();
console.log(x) // includes 'ConcreteType1'
if (x instanceof ConcreteType1) {
  // typescript lets you use Interface1 fields in here
}

But the best, most idiomatic way to deal with type shapes and interfaces at runtime is to use discriminating unions.

Simply add a common field, in this example 'type', that is set to a string literal. Then if eventProps is a union type, eventProps.type is 'interface1' | 'interface2' which makes testing at runtime and narrowing the type easy.

interface Interface1 {
  type: 'interface1';
  /* some fields */
};

interface Interface2 {
  type: 'interface2';
  /* some fields */
}

const x: Interface1 | Interface2 = getOne();
console.log(x) // includes { type: 'interface1', ... } so you can tell
if (x.type === 'interface1') {
  // typescript lets you use Interface1 fields in here
}

Upvotes: 1

Related Questions