0

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

3
  • 1
    NonNullable means it's not nullable. It doesn't mean that an optional property becomes mandatory. Commented Oct 13, 2022 at 15:50
  • 4
    Required built-in type Commented Oct 13, 2022 at 15:52
  • 4
    "OH NOEZ" looked strangely familiar Commented Oct 13, 2022 at 15:56

1 Answer 1

1

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

Sign up to request clarification or add additional context in comments.

4 Comments

Required<{ [K in keyof T]: NonNullable<T[K]> }> is excessive, you just need Required<T>
@VLAZ Then null would pass through. I'm not sure if they intended for null to also be excluded but that is what NonNullable does.
@caTS ah, I missed that null wasn't needed. I was just going off MyType. In that case, it would be shorter to use { [K in keyof T]-?: NonNullable<T[K]> } (the extra -? to make properties not optional) than Required. Not much difference in bytes but it's an option.
I prefer to use Required instead of -? in term of readability. Thanks guys, I learned new things today

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.