2

How can I use an optional type parameter in a type constructor? In the following, I want the Fruit type constructor to return different types based on whether C is passed or not:

type Color = 
  | 'yellow' 
  | 'orange'
  | 'red';

type Fruit<T, C> = {
           // ^ I wanna make this optional
  t: T,
  color: C, // <- should be optional if there's no C
};

type Orange = Fruit<'orange', Color>
type Apple = Fruit<'apple'>;

// √
const orange: Orange = { t: 'orange', color: 'orange' }; 

// Error: Cannot use `Apple` with less than 2 type arguments.
const apple: Apple = { t: 'apple' }; 
Sam R.
  • 14,850
  • 9
  • 56
  • 106

1 Answers1

3

I think I can use default types with empty to accomplish this, but I'm not sure if it's the best way:

type Color = 
  | 'yellow' 
  | 'orange'
  | 'red';

type Fruit<T, C = empty> = {
  t: T,
  color?: C,
};

type Orange = Fruit<'orange', Color>
type Apple = Fruit<'apple'>;
type Banana = Fruit<'banana', number>;

const orange: Orange = { t: 'orange', color: 'orange' }; // √
const apple: Apple = { t: 'apple' };                     // √
const banana: Banana = { t: 'banana', color: 1 };        // √
Sam R.
  • 14,850
  • 9
  • 56
  • 106
  • UGH, this will break if I do `const getColor = (o: Orange): Color => o.color;` – Sam R. Feb 19 '18 at 18:55
  • 1
    Color needs to also handle void, then your getColor method passes. [flow.rg/try](https://flow.org/try/#0C4TwDgpgBAwg9gGzgJygXigKClAPlAchAgSQHcCsd8CUBDAOwHMIDs9DkIATN6qAG5wAltwDcmTKEhQAYsgCuw4AB4AKgBpY6KBAC2YUAD4dAb3bAAXFE3sAxohQB+azA2YAvhKnhoAeWRGFh15JVVaQOZWLXgkZCMfGQBBMDAEaAxQ5RUCOlT0giMJaWgAIUYKkMVsggAjCoY6Ai0GBT1aiHjvBwYAZ2AoeijrAKCMqFMoK0IhlmaoBzjrCLHKLygAeg2oQCwiTB7+qDy0iGsUk7Mp5eOCqHWcB8en563d-bg+gfrGxutyn7ol2mdQaTS0ixQ1gAjHcxM8cK89gcBixgLEUDoABRwEaRFgASlcjlQaBMcAAdBDkHD4QjtjsgA) – Dave Meehan Feb 20 '18 at 12:10