Type Object includes string?

Viewed 422
function isObject(v: any): v is Object{ 
    return v instanceof Object && !Array.isArray(v);
}


let v: string | Object;
if (isObject(v))
    v.hasOwnProperty("x");
else
if (typeof v === 'string')
    v.trim();             //Error: Property 'trim' does not exist on type 'never'

I am confused with that error in the last line, apparently, v gets narrowed to type 'never" but I can't see the logic of it. (Typescript version 4.2.3)

4 Answers

This is how Object interface defined in typescript:

interface Object {
    constructor: Function;
    toString(): string;
    toLocaleString(): string;
    valueOf(): Object;
    hasOwnProperty(v: PropertyKey): boolean;
    isPrototypeOf(v: Object): boolean;
    propertyIsEnumerable(v: PropertyKey): boolean;
}

Almost any type including primitives like string is assignable to Object (yes string have all of the above properties).

object (lowercase) is a type that represents the non-primitive type, i.e. anything that is not number, string, boolean, symbol, null, or undefined

function isObject(v: any): v is object {
    return v instanceof Object && !Array.isArray(v);
}

declare let v: string | object;
if (isObject(v))
    v.hasOwnProperty("x");
else
    v.trim(); // v narrowed to string

Playground

Do's and Don'ts

let v: string | Object;

While executing the instanceOf(type checker) is considering v variable type as string because of you mentioned string as first type and Object is a second type of that variable. So in this case without having any value of that variable how the instanceOf know the type?. So it will check null instanceof Object. Which means null is not an object type. This is the reason to why isObject() return false. See

console.log(null instanceof Object)

If you assign any value, then only the instanceOf will be considered the type based on value because of that variable have two data types.

I think the explanation for this seemingly weird behaviour is that object != Object. Whereas object is a type that represents all non primitive types (not a string) the Object type is just an interface that contains common methods around javascript Objects like toString() etc.

In the function signature there is:

v is Object

This will just check whether your variable type implements all methods of the Object interface. Actually for string, this is the case and therefore Typescript thinks:

Wait, he is providing me with a variable that is either Object or string and both of them implement the Object interface, thus the method will return true every time. Thats why it is never in the else case of the if.

If you use object instead of Object this will be fixed because object is a type that represents all non primitive types (not string). Therefore, typescript doesn't think that your function will return true all the time like with Object.

const a: Object = '1'; // OK
const b: Object = 1; // OK
const c: Object = false; // OK
const d: Object = {}; // OK
const e: Object = undefined; // Error
const f: Object = null; // Error

const g: object = '1'; // Error
type T1 = string extends Object ? boolean : number; // boolean
type T2 = Number extends Object ? boolean : number; // boolean
...
type T3 = undefined extends Object ? boolean : number; // number
type T4 = null extends Object ? boolean : number; // number

That is to say, the Object is type of ONLY the members defined. and every type extends Object except for undefined and null

SO

v is Object ===> v is object

Related