Discriminate between empty object type and other concrete types

后端 未结 2 1600
轮回少年
轮回少年 2021-01-21 00:15

All of this compiles without error:

interface Empty { }
interface MaybeEmpty { a?: number; }

var one: Object = 20;
var two: Empty = 21;
var three: {} = 22;
var          


        
2条回答
  •  慢半拍i
    慢半拍i (楼主)
    2021-01-21 00:56

    Update for TS2.4+, there is now weak type detection which will prevent assigning to all-optional-property types like MaybeEmpty if there are no overlapping properties, so var four: MaybeEmpty = 23 is now not permitted.


    Update for TS2.2+, there is now an object type which specifically excludes primitives. Object-like types using curly braces like {} or { a?: number } are still compatible with primitives, as mentioned below.


    Original answer:

    No.

    The root problem is that JavaScript often treats primitive values as if they were their Object-typed equivalents (String/Number/etc) when used in a context where an object is required (e.g. property access).

    TypeScript models this using the "apparent type" whereby objects of a primitive type appear to have the members of their corresponding Object types. This is because, intuitively, code like this should compile without error:

    function fn(x: { toString(): string }) { /* ... */ }
    fn(32); // Should be OK because (32).toString() is OK
    fn('foo'); // Should be OK because ('foo').toString() is OK
    

    It's a small step to this code:

    function fn(x: { toString(): string; other?: whatever; }) { /* ... */ }
    fn(32); // Should be OK
    fn('foo'); // Should be OK
    

    And another small step to this code:

    function fn(x: { other?: whatever; }) { /* ... */ }
    fn(32); // OK, I guess?
    fn('foo'); // OK, I guess?
    

    There's an issue on GitHub tracking the general problem that an interface with all optional properties is effectively never the source of an error when assigning to it. It's a very hard thing to spec.

提交回复
热议问题