Search code examples
typescriptconditional-types

Why is this conditional generic type not correctly narrowed?


I have the following type:

type GetNum<T extends number[] | number> = T extends number ? T : T['length']

But it generates a compilation error:

Type '"length"' cannot be used to index type 'T'.

Why is that? The generic type parameter is constrained to be either a number or number[] array. In the true arm of the conditional type, I check whether it's a number, so in the false arm the type should be narrowed to number[], and therefor have a length property. Yet, I get the error mentioned above.

Why doesn't this work? Is there any relevant information for reference?


Solution

  • You could get that to compile if you reverse the condition in your conditional type:

    type GetNum<T extends number[] | number> = T extends number[] ? T['length'] : T;
    

    Sinces types don't direct runtime behavior, it doesn't really matter all that much though. T['length'] just resolves to number so, you could just have written:

    type GetNum<T extends number[] | number> = T extends number ? T : number;
    

    There are a number of issues on the TypeScript GitHub repository that are related: #21937, #29188, #48710, #51040, and probably others. Since there's an easy workaround, the TypeScript team doesn't seem to be in a rush to address this, as this comment expresses:

    In the general case, it's not sound to narrow the false arm of a conditional type. When the check type fully subsumes one of the constraint's union constituents we could, but that logic just isn't present right now. Regardless, the workaround is straightforward.