Tiamanti
Tiamanti

Reputation: 23

Why doesn't "in" narrow type for complex object?

I stumbled on issue in Typescript when I'm doing type narrowing using in for a complex object it doesn't properly narrow type unless I first extract it proxy variable.

type Animal = {
  fish: Fish;
  bird: Bird;
};
type Fish = {
  swim: () => void
};
type Bird = {
  fly: () => void
};

const globalA: Animal = {} as unknown as Animal;

function useDirectly(typew: keyof Animal) {
  if ("swim" in globalA[typew]) {
    return globalA[typew].swim();
  }

  return globalA[typew].fly();
}

function useProxyVariable(typew: keyof Animal) {
  const temp = globalA[typew];
  if ("swim" in temp) {
    return temp.swim();
  }

  return temp.fly();
}

Example on TS Playground

Does anyone know why is this happening and if there is a way to do type narrowing without using those proxy objects?

Upvotes: 2

Views: 69

Answers (2)

Andrew Shepherd
Andrew Shepherd

Reputation: 45232

The type returned from globalA[typew] depends upon two variables, globalA and typew.

You are asking if you can add extra levels of indirection when doing type narrowing. It's quite reasonable for the compiler to not support this.

A programmer could attempt to fool it by changing the variables involved in the indirection.

function useDirectly(typew: keyof Animal) {
  if ("swim" in globalA[typew]) {
    typew = 'bird';
    return globalA[typew].swim(); // HAHA take that compiler I tricked you!
  } else {
    return v.fly();
  }

Upvotes: 0

T.J. Crowder
T.J. Crowder

Reputation: 1074168

Does anyone know why is this happening and if there is a way to do type narrowing without using those proxy objects?

It's just a limitation of the TypeScript compiler, it narrows the type of variables, not expressions. globalA[typew] is an expression. To narrow expressions like that, it would have to keep track of not just globalA but also typew. While it's easy for us to see that both of them are unchanged between the guard and the usage, this is just one of several pragmatic limitations of the compiler.

Using a temporary variable/constant as you've shown is how you get around it (and saves having to retype the expression).


Side note: I wouldn't use the term "proxy variable" (because of proxies); it's just a variable (well, a constant in your temp case😃).

Upvotes: 1

Related Questions