Search code examples
typescriptassertion

Typescript assert function to avoid Non-Null Assertion Operator ! with Objects


based on this post Typescript - What is the best way to type check an object properties and throw upon null?

I'm trying to check if values of my object are not undefined and narrow the type of the argument to a version with all non-null properties but I keep having this issue: "Argument of type '{ Id?: string | undefined; Name?: string | undefined; }' is not assignable to parameter of type 'MyTypeNotNull'."

I would like to know what's wrong with my assert function signature ?

type MyType = {
    Id?: string | undefined
    Name?: string | undefined
};

type MyTypeNotNull = {
    Id: string
    Name: string
};

function logMyType(type: MyTypeNotNull) {
    console.log(type);
}

function assertNoPropsAreUndefined<T extends object>(obj: T): asserts obj is
    { [K in keyof T]: NonNullable<T[K]> } {
    Object.entries(obj).forEach(([k, v]) => {
        console.log(k, v)
        if (v === undefined)
            throw new Error("OH NOEZ, PROP \"" + k + "\" IS NULL");
    });
}

const run = (value: MyType) => {
    value; // (parameter) value: MyType
    assertNoPropsAreUndefined(value)
    value;
    logMyType(value); // should be ok but it throw Argument of type '{ Id?: string | undefined; Name?: string | undefined; }' is not assignable to parameter of type 'MyTypeNotNull'.
};

run({ Id: 'myKey1', Name: "ok" }); // {key1: "myKey1"}

run({ Id: 'myKey1', Name: undefined }); // 💥 OH NOEZ, PROP "key3" IS NULL

Playground link to code


Solution

  • Thanks @caTS. For others in the same situation I was missing the Required built-in type.

    type MyType = {
        Id?: string | undefined
        Name?: string | undefined
    };
    
    type MyTypeNotNull = {
        Id: string
        Name: string
    };
    
    function logMyType(type: MyTypeNotNull) {
        console.log(type);
    }
    
    function assertNoPropsAreUndefined<T extends object>(obj: T): asserts obj is
        Required<{ [K in keyof T]: NonNullable<T[K]> }> {
        Object.entries(obj).forEach(([k, v]) => {
            if (v === undefined)
                throw new Error("OH NOEZ, PROP \"" + k + "\" IS NULL");
        });
    }
    
    const run = (value: MyType) => {
        value; // (parameter) value: MyType
        assertNoPropsAreUndefined(value)
        value;
        logMyType(value);
    };
    
    run({ Id: 'myKey1', Name: "ok" }); // {key1: "myKey1"}
    
    run({ Id: 'myKey1', Name: undefined }); // 💥 OH NOEZ, PROP "Name" IS NULL
    

    Link to playground