Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript error because nested value might be null even after I check it's not null [duplicate]

I am having a problem with Drizzle response and typescript. Drizzle joins return an object with nested object | null, and I couldn't find a "typescript" only way to resolve it without doing extra unnecessary steps that change the code just to make typescript happy.

The following code simulate the type issue:

interface Author {
    id: string;
}

interface Post {
    id: string;
    author: Author;
}

interface PostWithNull {
    id: string;
    author: Author | null;
}

const mixedData = [
    {
        id: '123',
        author: null,
    },
    {
        id: '234',
        author: {
            id: '1'
        },
    }
];

function getPostById(id: string): Post | null {
    // Simulate Drizzle response type -> DO NOT CHANGE
    const res = mixedData.find((record) => record.id === id) as PostWithNull;
    if (!res) {
        return null;
    }

    // The problematic part
    if (res.author) {
        return res;
        // The code below will work.
        // return {
        //     ...res,
        //     author: res.author
        // }
    }


    return null;
}

Removing the "as" will resolve the issue, but that's not the point, as this is just to simulate the response I am getting, I have no control over it. I can copy the object like I did there, but it would be an extra step that I am doing only for Typescript compiler, and I'd rather avoid.

I am looking for a Typescript solution to this problem.

like image 729
eliezra236 Avatar asked Dec 07 '25 03:12

eliezra236


1 Answers

That check should be enough to narrow the type of res but the compiler is being obtuse here for some reason. I'd just use the clone example you have commented out. Alternatively you could make an isPost type guard:

function isPost(x: unknown): x is Post { // note the 'is' in the type
    return Boolean(
        x 
        && typeof x === 'object' 
        && 'id' in x
        && typeof x.id === 'string' 
        && 'author' in x 
        && x.author 
        && typeof x.author === 'object'
        && 'id' in x.author 
        && typeof x.author.id === 'string'
    )
}

function getPostById(id: string): Post | null {
    // Simulate Drizzle response type -> DO NOT CHANGE
    const res = mixedData.find((record) => record.id === id) as PostWithNull;
    if (!isPost(res)) {
        return null;
    }

    return res; // no error
}

Playground

While it's a bit verbose it will a) convince the compiler and b) give you even more surety because of the runtime checks.

like image 94
Jared Smith Avatar answered Dec 08 '25 18:12

Jared Smith