Is TypeScript incapable of using ReturnType on union types?
type NumberParser = (input: string) => number | DiplomacyError;
type StringParser = (input: string) => string | DiplomacyError;
type Parser = NumberParser | StringParser;
export interface Schema {
[key: string]: Parser | Schema;
}
export type RawType<T extends Schema> = {
[Property in keyof T]: T[Property] extends Schema
? RawType<T[Property]>
: ReturnType<T[Property]>; // T[Property] marked as error
};
<T[Property]> gives the following error:
Type 'T[Property]' does not satisfy the constraint '(...args: any) => any'.
Type 'T[keyof T]' is not assignable to type '(...args: any) => any'.
Type 'T[string] | T[number] | T[symbol]' is not assignable to type '(...args: any) => any'.
Type 'T[string]' is not assignable to type '(...args: any) => any'.
Type 'Parser | Schema' is not assignable to type '(...args: any) => any'.
Type 'Schema' is not assignable to type '(...args: any) => any'.
Type 'Schema' provides no match for the signature '(...args: any): any'.ts(2344)
It is a known issue in TypeScript that the false branch of a conditional type does not get its types narrowed. So in
T extends U ? F<T> : G<T>does not takeG<T>and replace it with something likeG<Exclude<T, U>>. As far as the compiler is concerned, theTinG<T>might still be assignable toU, even though it's obvious to us that it won't be. See microsoft/TypeScript#29188. It looks like there was some work done to address this at microsoft/TypeScript#24821, but it was not merged. It's not clear to me if or when this issue will be resolved.Until then, it's easy enough (if annoying) to do such narrowing yourself when necessary:
Or possibly
Playground link to code