Search code examples
typescriptasserttype-narrowing

Most precise type after checking `typeof a === 'object' && a !== null`


I have this assertion function, which checks that the passed value is an object (according to the typeof operator), but excluding null:

export function assertIsJavaScriptObjectExceptNull(value: unknown) {
    if (typeof value !== 'object' || value === null) {
        throw Error('Value is not an object.');
    }
}

I'd like to add an assertion signature to this function, so that it helps with type narrowing when used. What is the "most correct" type to use here?

export function assertIsJavaScriptObjectExceptNull(value: unknown): asserts value is <type> {
    if (typeof value !== 'object' || value === null) {
        throw Error('Value is not an object.');
    }
}

Is it {[key: string | number | symbol]: unknown}, or Record<keyof unknown, unknown>, or Object, or something else?

I'm looking for the type that matches TypeScript's understanding of what this type should be. I know that TypeScript has many settings which can slightly influence this and that JavaScript engines have quirks and bugs that might lead to a result that contradict this understanding. So there might not be a single best answer.


Solution

  • What are you looking for is the object type, which represents any non-primitive type. Please, avoid using Object which is a JavaScript class and is recommended to be avoided in the official Do's and Don'ts.

    object allows us to make sure that the value assigned to a type is not primitive:

    const noError: {toString(): string} = 1
    const withError: object & {toString(): string} = 1;
    

    Basically, you can intersect anything with object to make sure that the type doesn't allow primitive values.

    For a better understanding of the sets theory in the typescript, refer to this blog post