Search code examples
typescriptforeachpromisetype-inferencetypescript-never

TypeScript sets type 'never' for resolved value after Promise.allSettled().forEach() processing


TypeScript is giving me a type 'never' after a Promise.allSettled(promises).forEach(results)?

type myValue = {
        test1: string;
        test2: string;
        test3: string;
    } | undefined;


const go = async () => {

    // the values
    let value: myValue = undefined;
    let value2: myValue = undefined;

    // do some async requests
    const results = await Promise.allSettled([
        Promise.resolve({test1: 'test1.1', test2: 'test2.1', test3: 'test3.1'}),
        Promise.resolve({test1: 'test1.2', test2: 'test2.2', test3: 'test3.2'}),
    ]);

    // process results
    results.forEach( (result, index) => {
        if( result.status === "fulfilled") {
            if( index === 0 ) {
                // set value
                value = result.value;
            }
            if( index === 1 ) {
                // set value
                value2 = result.value;
            }
        }
        if( result.status === "rejected") {
            // process error
            console.error("error", result.reason);
        }
    });

    // checking if value is set
    if( !value || !value2 ) {
        throw new Error("no Value found");
    }

    // with TypeScript errors
    console.log("the type given is 'never', but value.test1 is set:", value.test1);
    console.log("the type given is 'never', but value2.test3 is set:", value2.test3);
}

go();

TS playground: Visit

I have tried:

  • comments: @ts-ignore / @ts-expect-error / etc but I don't want to do this, because I need a lot of them.
  • typecasting: value = value as MyValue; will also make the error dissapear, but should not be needed here

Why is TypeScript not inferring the type here?


Solution

  • Internally, TypeScript "stores" the value of value and value2 to be undefined, even though the type is MyValue. Since TypeScript thinks it's undefined and you don't reassign it in that scope TypeScript still thinks it's undefined when you perform the check !value || !value2. Because TypeScript thinks it's undefined, it deduces that this condition will always be true and thus value and value2 below can "never" run. At least, that's what the compiler thinks.

    Yes, you do actually reassign the values later, but the compiler just can't see that yet.

    You actually have a very easy way to fix it - just remove the explicit initial value of undefined:

    let value: MyValue;
    let value2: MyValue;
    

    Playground