How to fix "Type 'string | boolean' is not assignable to type 'never'. Type 'string' is not assignable to type 'never'"?

interface IData {
    title: string;
    slug: string;
    published_at: string;
    isPopular: boolean;
}

const product: IData = {
    title: "Title 1",
    slug: "title-1",
    published_at: "2012",
    isPopular: true,
};

function getCourseBySlug(fields: Array<keyof IData>) {
    const items = <IData>{};
    // // Ensure only the minimal needed data is exposed
    fields.forEach((field) => {
        if (field === "isPopular") {
            items[field] = product.isPopular;
        }
        if (typeof product[field] !== "undefined") {
            items[field] = product[field]; // I am getting error here items[field]
        }
    });
    return items;
}

I am getting the error at "items[field]". I already tried a few solutions to other similar questions. But nothing worked for me. What I am missing here?
Error message: Type ‘string | boolean’ is not assignable to type ‘never’. Type ‘string’ is not assignable to type ‘never’

>Solution :

There are two distinct issues:

  1. Your items is not a valid IData object. It doesn’t have all of the required IData properties.

  2. In your loop, TypeScript doesn’t know the specific type of field (just that it’s one of keyof IData) and so doesn’t know whether items[field] is string or boolean; as a result, you can’t assign to it. Even though you know that items[field] and product[field] will have the same type, TypeScript doesn’t.

The first issue is simple: Define it as a partial IData:

const items: Partial<IData> = {};

You have at least three options for fixing #2:

  • Make the function fully typesafe with a helper.

    function copyProp<ObjType extends object, KeyType extends keyof ObjType>(obj: ObjType, key: KeyType, value: ObjType[KeyType]) {
        obj[key] = value;
    }
    function getCourseBySlug(fields: Array<keyof IData>) {
        const items: Partial<IData> = {};
        for (const field of fields) {
            copyProp(items, field, product[field]);
        }
        return items;
    }
    

    That works because copyProp ensures that the value passed in (product[field]) is type-compatible with the target object’s field via ObjType[KeyType].

    Playground link

  • Tell TypeScript to ignore the error. This is rarely the right thing to do, but sometimes in cases where you know the error isn’t important, it’s not unreasonable.

    function getCourseBySlug(fields: Array<keyof IData>) {
        const items: Partial<IData> = {};
        for (const field of fields) {
            // @ts-ignore
            items[field] = product[field];
        }
        return items;
    }
    

    Playground link

  • You could do an end-run around it by hiding the issue behind Object.fromEntries:

    function getCourseBySlug(fields: Array<keyof IData>) {
        const items: Partial<IData> = Object.fromEntries(fields.map(field => [field, product[field]]));
        return items;
    }
    

    Playground link

The first of those is fully typesafe (I think). The second two are both imperfect, but in a very contained way. getCourseBySlug is a small function and within it you know that items[field] and product[field] have the same type.

Note that in all of those, I removed your typeof product[field] !== "undefined" test. There’s no reason for it, the type of fields ensures that field is a valid key for product, and none of IData‘s properties is optional.

Leave a Reply