typescripttypescript-utility

Why {} (object) does not allow 'null' and 'undefined'?


Looking at the utility type 'NonNullable' of the type script, it is implemented as this.

/**
 * Exclude null and undefined from T
 */
type NonNullable = T & {}; 


type Object1 = {};    
type Object2 = object;

const testObj1: Object1 = {
    test: 'test',
}; // ok
const testObj2: Object2 = {
    test: 'test',
}; // ok

const testStr1: Object1 = ''; // ok
const testStr2: Object2 = ''; // Type 'string' is not assignable to type 'object'.ts(2322) 

{} Instead, what happens inside the type script when extends the 'object'?

Why does the type checker detect differently as in the example above?

I'd appreciate it if you could answer my questions, thank you!


Solution

  • An object literal type, written as curly braces containing zero or more property declarations, is an assertion that the value of that type

    1. Can have properties accessed at runtime, and
    2. Has the specified properties, with the specified types.

    Now, the empty object literal type {} in particular has no specified properties, so only (1) is in play. That is, {} is the type of anything that can have properties accessed at runtime. This includes object literals (naturally), but it also includes strings, numbers, and Booleans. That is, all of the following are undefined at runtime, not a runtime error:

    However, null and undefined are unique in that we can't even try to access properties on them. null.foo and undefined.foo are runtime errors, not just undefined. So null and undefined cannot be assigned to any object literal, not even {}, since they don't satisfy condition (1) above.

    object, on the other hand, is defined to be the type of all non-primitive things in Javascript. Javascript defines seven primitive types

    And Typescript defines object to be: every value which is not one of these seven things.

    Typescript also defines Object with a capital "O". This is the type of all Javascript values that satisfy the interface defined by the Object type.

    Generally, that's going to be very similar to {}, as any object that is not null or undefined in Typescript is going to (directly or indirectly) have Object as a prototype and hence will inherit all of the built-in Object properties. But we can break that. For instance, Object defines toString(): string, so { toString: function() { return 7; } } is a value which is not of type Object, since its toString is incompatible with that of Object. Though that value is of type {} (since it can have its properties accessed) and of type object (since it is not a primitive value).

    Most Typescript style guides recommend avoiding these types, as they cause confusion and are honestly seldom what you want. I mean, when do you actually mean "I want this function to accept any value, anything at all, but I specifically forbid null and undefined"? I recommend the following guidelines.