Search code examples
javascripttypescripttypestypescript-typingstsc

Declaring intersecting types in Typescript?


I've started learning typescript from the official documentation, and came across the topic of intersecting types. So, in order to get a proper understanding of how type intersection works, i decided to write up a custom type. The FoodItems interface requires to be an array of objects and same for side. However, whenever I try intersect the two types, every representation of FoodOrder fails. I don't understand why that happens and what i'm doing wrong.

interface FoodItems {
   [index: number]: {name: string, quantityPerOrder: number};
}

interface FoodSide {
    [index: number]: {sideName: string, sideQuantity: number};
}

let foodItem: FoodItems = [{name: "Fried Chiken", quantityPerOrder: 2}, {name: "Burger", quantityPerOrder: 1}];
let foodSide: FoodSide = [{sideName: "gravy", sideQuantity: 1}];

type FoodOrder = FoodItems & FoodSide;

let food: FoodOrder;

The declaration
let food: FoodOrder = [[{name: "Fried Chiken", quantityPerOrder: 2}],[{sideName: "gravy", sideQuantity: 1}]];
gives me the following error:

index.ts:14:24 - error TS2322: Type '[{ name: string; quantityPerOrder: number; }]' is not assignable to type '{ name: string; quantityPerOrder: number; } & { sideName: string; sideQuantity: number; }'.
  Type '[{ name: string; quantityPerOrder: number; }]' is not assignable to type '{ name: string; quantityPerOrder: number; }'.

14 let food: FoodOrder = [[{name: "Fried Chiken", quantityPerOrder: 2}],[{sideName: "gravy", sideQuantity: 1}]];
                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

index.ts:14:70 - error TS2322: Type '[{ sideName: string; sideQuantity: number; }]' is not assignable to type '{ name: string; quantityPerOrder: number; } & { sideName: string; sideQuantity: number; }'.
  Type '[{ sideName: string; sideQuantity: number; }]' is not assignable to type '{ name: string; quantityPerOrder: number; }'.

14 let food: FoodOrder = [[{name: "Fried Chiken", quantityPerOrder: 2}],[{sideName: "gravy", sideQuantity: 1}]];

Solution

  • An intersection type requires food to be both a valid FoodItems object and a valid FoodSide object.

    Remember: an intersection type I = A & B means that the set of values of type I is the intersection of the sets of the values of types A and B. Since every value of type I is at the same time a valid A and a valid B, it follows that the properties of the intersection type I must be the union of the properties of the types A and B.

    Likewise, a union type U = A | B means that the set of values of type U is the union of the sets of the values of types A and B. Since every value of type U is either a valid A or a valid B, it follows that the properties of the union type U must be the intersection of the properties of the types A and B.

    So, what you are saying is that FoodOrder is a type which can be indexed by a number and it returns a FoodItems but at the same time it also returns a FoodSide when it is indexed by a number.

    A value of such a type cannot exist, so you actually can't construct anything that would be legal to assign to food.

    What would be possible is something like this:

    interface FoodItem {
        name:             string
        quantityPerOrder: number
    }
    
    interface FoodSide {
        sideName:         string
        sideQuantity:     number
    }
    
    interface FoodOrder {
      [index: number]: FoodItem & FoodSide
    }
    
    let food: FoodOrder;
    
    food = [
        {
            name:             "Fried Chiken", 
            quantityPerOrder: 2, 
            sideName:         "gravy", 
            sideQuantity:     1
        }
    ];