Here's a short sample of what I'm trying to do:
import { connect } from "react-redux";
interface ErrorProps {
error: true;
description: string;
}
interface NoErrorProps {
error: false;
}
type TestProps = ErrorProps | NoErrorProps;
function Test(props: TestProps) {
return props.error ? <>Error: ${props.description}</> : <>OK</>;
}
// This is a test implementation that always returns no error
connect(() => ({ error: false }))(Test);
However, I get an error:
Argument of type '(props: TestProps) => Element' is not assignable to parameter of type 'ComponentType<Matching<{ error: boolean; } & DispatchProp<AnyAction>, TestProps>>'.
Type '(props: TestProps) => Element' is not assignable to type 'FunctionComponent<Matching<{ error: boolean; } & DispatchProp<AnyAction>, TestProps>>'.
Types of parameters 'props' and 'props' are incompatible.
Type 'PropsWithChildren<Matching<{ error: boolean; } & DispatchProp<AnyAction>, TestProps>>' is not assignable to type 'TestProps'.
Type 'Matching<{ error: boolean; } & DispatchProp<AnyAction>, NoErrorProps> & { children?: ReactNode; }' is not assignable to type 'TestProps'.
Type 'Matching<{ error: boolean; } & DispatchProp<AnyAction>, NoErrorProps> & { children?: ReactNode; }' is not assignable to type 'NoErrorProps'.
Types of property 'error' are incompatible.
Type 'boolean' is not assignable to type 'false'.
Now, I have used algebraic data types for a long time and usually am pretty confident that I understand what I'm doing. But here I'll explain my reasoning in (probably) painfully obvious detail because I'm stumped and suspect that I misunderstand something very basic and fundamental.
I'm trying to decipher this error message, and while I'm not entirely sure what's happening on the left part of the type comparison (with Redux types), it seems that every line is a reason for the line before it. So, I look at these two lines, disregarding the types on the left:
Type '...' is not assignable to type 'TestProps'. Type '...' is not assignable to type 'NoErrorProps'.
And I notice that this means that whatever '...' means it does not change, so here I disregard it. But this sequence is not assignable to TestProps
because it is not assignable to 'NoErrorProps'. Which means for something to be assignable TestProps
, it must be assignable to NoErrorProps
.
Which doesn't make any sense. I just checked that I'm still sane:
const _1: TestProps = { error: false };
const _2: TestProps = { error: true, description: "foo" };
First variable is not assignable to ErrorProps
, second is not assignable to NoErrorProps
, but both are perfectly assignable to TestProps
, because that's how union types work. Why do the lines in the error message above imply the opposite?