node.jstypescriptdiscord.jstypescript4.0

Typescript Conditional Statement doesn't filter values?


When I use Typescript Conditional Statement it doesn't remove the values. For example, if the type of "ErrorsType" is string | null, and I use ErrorsType extends string ? InHereTheValueOf'ErrorsType'IsStill'String'|'null'InsteadOf'String' : InHereTheValueOf'ErrorsType'IsStill'String'|'null'InsteadOf'null'

Here is my code:

export const BotErrors = {
    test: (reason: string) => `This error bc of ${reason}`,
    ValueTooLow: 'You can not reduce value below 0',
};

type ErrorsType = typeof BotErrors;

export default class SomeError<T extends keyof ErrorsType> extends Error {
    constructor(code: T, ...args: ErrorsType[T] extends Function ? Parameters<ErrorsType[T]> : []) {
        let msg: string | Function = BotErrors[code];
        if (typeof msg === 'function') msg = msg(...args) as string;
        super(msg);
    }
}

At ErrorsType[T] got error: Type 'Function & { test: (reason: string) => string; ValueTooLow: string; }[T]' does not satisfy the constraint '(...args: any) => any'. Because of the BotErrors have both function and string as it's value.

After I add // @ts-nocheck everything is fine, even with type definition. But I'm try wondering if there is any way to not ignore errors?


Solution

  • The problem with

    ErrorsType[T] extends Function ? Parameters<ErrorsType[T]> : []
    

    is that the Parameters<T> utility type is defined as

    type Parameters<T extends (...args: any) => any> = 
       T extends (...args: infer P) => any ? P : never;
    

    where its type parameter is constrained to the function type expression (...args: any) => any,

    while you have only checked the ErrorsType[T] type argument against the Function interface. And while (...args: any) => any is assignable to Function, the reverse is not true: just because something is a Function the compiler does not know that it is a (...args: any) => any. The Function type is best avoided in general, since it represents untyped function calls.

    Anyway it's easy enough to fix this; just change the conditional type check from Function to (...args: any) => any:

    ErrorsType[T] extends (...args: any) => any ? Parameters<ErrorsType[T]> : []
    

    Or the equivalent inlined version:

    ErrorsType[T] extends (...args: infer P) => any ? P : []
    

    This makes the error you mentioned go away. Note that once this is resolved your example code has other errors, but those are out of scope for the question as asked.

    Playground link to code