1

i'm trying to encapsulate some logic inside component OrderGuard ( which can work with two type of orders: CheckinOrder | Checkout order, but, when i'm try to pass order to orderLoad callback, typescript start complain like so

CheckinOrder is assignable to the constraint of type T but T could be instantiated with a different subtype of constaint 'Order'

type Order = CheckoutOrder | CheckinOrder;

interface Props<T extends Order> {
    orderId: string;
    orderLoaded: boolean;
    onOrderLoad: (order: T) => void;
    loadOrder: UseCheckinOrder | UseCheckoutOrder;
    children?: React.ReactElement;
}

const isCheckinOrder = (order: Order): order is CheckinOrder => {
    return !('servicesFallbackURL' in order);
};

const OrderGuard: <T extends Order>(props: Props<T>) => React.ReactElement<Props<T>> = ({
    orderId,
    orderLoaded,
    onOrderLoad,
    loadOrder,
    children
}) => {
    const [userHasAccess, setUserHasAccess] = useState(true);
    const { refetch, loading } = loadOrder(orderId, { skip: true });

    const handleOrderLoad = (order: Order) => {
        if (isCheckinOrder(order)) {
            onOrderLoad(order as CheckinOrder); // <-- error here
        } else {
            onOrderLoad(order as CheckoutOrder); // <-- and here
        }
    };

i think i miss something but can't figure out what I'm pretty new in typescript, how others handle this situations?

component call looks like

    <OrderGuard<CheckoutOrder>
        orderId={orderId}
        orderLoaded={!!order}
        onOrderLoad={fillOrder}
        loadOrder={useOrder}
    >
        <Checkout startNewSearch={startNewSearch} />
    </OrderGuard>
  • Relevant: https://stackoverflow.com/q/56505560/251311 – zerkms Nov 14 '19 at 08:48
  • Can't you just `const handleOrderLoad = (order: T) => onOrderLoad(order)` or just use `onOrderLoad` instead of `handleOrderLoad` (which does nothing in above example but delegating to `onOrderLoad`) – Aleksey L. Nov 14 '19 at 08:51
  • "how others handle this situations?" --- in this very case your `OrderGuard` is not generic. Just declare it as `const OrderGuard = (props: Props)` and you'll be alright, I believe. – zerkms Nov 14 '19 at 08:57
  • @AlekseyL. no, i getting same error when i'm trying to pass order to onOrderLoad function https://dropmefiles.com/SFio7 – Евгений Литвиненко Nov 14 '19 at 09:29
  • 2
    That type assertion is not necessary, since you've already put a type guard function ahead of it. – hackape Nov 14 '19 at 09:29

1 Answers1

0

Not making the Prop interface generic will fix your problem in this instance.

type Order = CheckoutOrder | CheckinOrder;

interface Props {
    orderId: string;
    orderLoaded: boolean;
    onOrderLoad: (order: Order) => void;
    loadOrder: UseCheckinOrder | UseCheckoutOrder;
    children?: React.ReactElement;
}

const isCheckinOrder = (order: Order): order is CheckinOrder => {
    return !('servicesFallbackURL' in order);
};

const OrderGuard: (props: Props) => React.ReactElement<Props> = ({
    orderId,
    orderLoaded,
    onOrderLoad,
    loadOrder,
    children
}) => {
    const [userHasAccess, setUserHasAccess] = useState(true);
    const { refetch, loading } = loadOrder(orderId, { skip: true });

    const handleOrderLoad = (order: Order) => {
        if (isCheckinOrder(order)) {
            onOrderLoad(order); // <-- no need to cast
        } else {
            onOrderLoad(order as CheckoutOrder); // <-- no more errors
        }
    };
FunkeyFlo
  • 295
  • 1
  • 6