7

Why is this legal TypeScript?

var x: number = 5
var y: Object = x

Surely a number is not an Object. One might suspect that x is implicitly coerced (auto-boxed) to an object, but no:

if (!(y instanceof Object)) {
   console.log(typeof y)
}

prints

number

For the record:

$ tsc --version
Version 1.8.10
Roly
  • 2,126
  • 2
  • 20
  • 34

1 Answers1

7

Type compatibility in TypeScript is based on structural subtyping, not nominal typing. That said, consider the two following interface definitions:

interface IFoo { X: number }
interface IBar { X: number; Y: number }

Does IBar extend IFoo? No.

But is IFoo compatible with IBar? Yes.

The members of IFoo are a subset of IBar members, thus you can assign any IBar to IFoo. But it doesn't work the other way around:

var x: IFoo;
var y: IBar;

x = y // all good
y = x // type error, x has no Y member

This way in Typescript all types are compatible with Object if you think of it as the empty interface. This way you can pass any valid typescript value to functions accepting Object and play well with the way Javascript libs are written.

I suggest reading Type Compatibility in docs and the last paragraph about Subtype vs Assignment.

krontogiannis
  • 1,819
  • 1
  • 12
  • 17
  • 1
    Ok, that's the essentially the explanation I was anticipating (after noticing that I could also assign a bool to an empty interface I defined called Bool, which is isomorphic to Object). I understand structural vs. nominal, but hadn't expected it to apply to non-reference types. I guess this is only sound to the extent to which Object really has an empty interface (given that there are probably things I can do to an object that I can't do to a number or primitive string). – Roly Sep 19 '16 at 08:35
  • I'm happy with the explanation, so I've accepted the answer. Turns out I was getting bitten not because number < Object (which seems OK, modulo my caveat above), but because subtyping of function types seems borked. Here's a new question: http://stackoverflow.com/questions/39569016. – Roly Sep 19 '16 at 08:55
  • 1
    I guess here's a sense in which number < Object is unsound: it breaks the upward-closure of instanceof. In other words, X < Y and x instanceof X should imply x instanceof Y. – Roly Sep 19 '16 at 09:04
  • 1
    I really understand your objections (can't disagree that TypeScript's type system presents some unsound cases), but usually the answer is (for good or bad), that it enables patterns common in Javascript :) Check also [function bivariance](https://www.typescriptlang.org/docs/handbook/type-compatibility.html#function-parameter-bivariance) in docs. – krontogiannis Sep 19 '16 at 09:17