Typescript - How can I conditionally define a type based on another property

Jake

I'm trying to conditionally set the return type of a function, based on a static value in the same type.

Given the following code, is it possible to have type checking work as I've specified in the comments?

IE, if const taskX: Task has the id: 'get-name', how can I make it so taskX.run() should return according to the type defined as Outputs['get-name']

type Outputs = {
  "get-name": {
    name: string;
  };
  "get-age": {
    age: number;
  };
  "get-favourite-fruits": ("apple" | "strawberry" | "melon")[];
};

type Task = {
  // This should always be a static property defined in `Outputs`
  id: keyof Outputs;
  // How can I make this conditional and return the type from `Outputs` based on `id`
  run: () => never; 
};

const taskOne: Task = {
  id: "get-name",
  // This should be valid
  run: () => {
    return { name: "frankenstein" };
  },
};

const taskTwo: Task = {
  id: "get-age",
  // This should also be valid
  run: () => {
    return { age: 22 };
  },
};

const taskThree: Task = {
  id: "get-favourite-fruits",
  // This should fail with
  // Type '("apple" | "banana")[]' is not assignable to type
  // '("apple" | "strawberry" | "melon")[]'
  run: () => {
    return ["apple", "banana"];
  },
};
jcalz

You want Task to be a discriminated union of the three different possible pairings of id and run. You can generate this programmatically from Outputs by mapping each property of Outputs to a value corresponding to this pairing, and then looking up all of the mapped properties to get the desired union:

type Task = {
  [K in keyof Outputs]: { id: K, run: () => Outputs[K] }
}[keyof Outputs];

/* produces
type Task = {
    id: "get-name";
    run: () => {
        name: string;
    };
} | {
    id: "get-age";
    run: () => {
        age: number;
    };
} | {
    id: "get-favourite-fruits";
    run: () => ("apple" | "strawberry" | "melon")[];
}*/

Then the rest of your code works as desired. Valid tasks are accepted:

const taskOne: Task = {
  id: "get-name",
  run: () => {
    return { name: "frankenstein" };
  },
}; // okay

const taskTwo: Task = {
  id: "get-age",
  run: () => {
    return { age: 22 };
  },
}; // okay

And invalid tasks are rejected:

const taskThree: Task = {
  id: "get-favourite-fruits",
  run: () => { // error!
    // Type '() => ("apple" | "banana")[]' is not assignable to type
    // '(() => { name: string; }) | (() => { age: number; }) | 
    // (() => ("apple" | "strawberry" | "melon")[])
    return ["apple", "banana"];
  },
};

const taskFour: Task = { // error!
//    ~~~~~~~~
// Type '{ id: "get-favourite-fruits"; run: () => { age: number; }; }'
// is not assignable to type 'Task'.
  id: "get-favourite-fruits",
  run: () => { 
    return { age: 123 };
  },
};

The errors for invalid tasks might be a bit different from the particular one you were expecting. For taskThree the compiler complains that run()'s return is incompatible with every possible Task member; "not only isn't it the right array, but it isn't even one of the right types for get-name or get-age!" For taskFour the compiler sees that run() returns an {age: number} but the id is get-favorite-fruits and complains that the whole value is wrong without highlighting any particular property.

Playground link to code

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

WIX: Can i define a property based on another property in WIX?

From Dev

How can I define a return type of void for a function in a Typescript interface?

From Dev

How can I define the return type of a lodash reduce function with Typescript?

From Dev

How can I define the return type of a lodash reduce function with Typescript?

From Dev

Can I define a record type in terms of another?

From Dev

How to conditionally deserialize JSON object based on another JSON property?

From Dev

How can I define a global property in rhino?

From Dev

How can I define a Typescript interface for a function?

From Dev

How can I define the width of one element based on another unrelated element?

From Java

How to define a Typescript object type based on existing object values?

From Dev

How can i define in xsd that a simple/complex type contain another XML?

From Java

Typescript: Can I define an n-length tuple type?

From Dev

Can I define a Typescript function parameter to have a type of boolean or string?

From Dev

With AngularJS, how can I conditionally add a class based on an action?

From Dev

How can I conditionally run a suite of tests based on iOS device?

From Java

How can I stop "property does not exist on type JQuery" syntax errors when using Typescript?

From Dev

How should I define the type of a promise call back in Typescript?

From Dev

How can I select an object type to deserialize using Json.Net based on property values in the data

From Dev

Can I define events in Typescript?

From Dev

How can I define a property type as being a list (list, set, array, collection) of string in my YAML Swagger definition

From Dev

How can I define a property type as being a list (list, set, array, collection) of string in my YAML Swagger definition

From Dev

How can I augment a typescript interface using a type exported from another .d.ts file?

From Dev

How Can I Create A Custom Property Type

From Dev

How can I declare the type of a class property?

From Dev

How can I solve "Cannot read property 'define' of undefined"?

From Dev

How can I define a property that returns the current value of a RadioButton in Kotlin?

From Java

How to define an opaque type in TypeScript?

From Dev

How can I define a Typescript object return value for a function?

From Java

How can I define an interface for an array of objects with Typescript?

Related Related

  1. 1

    WIX: Can i define a property based on another property in WIX?

  2. 2

    How can I define a return type of void for a function in a Typescript interface?

  3. 3

    How can I define the return type of a lodash reduce function with Typescript?

  4. 4

    How can I define the return type of a lodash reduce function with Typescript?

  5. 5

    Can I define a record type in terms of another?

  6. 6

    How to conditionally deserialize JSON object based on another JSON property?

  7. 7

    How can I define a global property in rhino?

  8. 8

    How can I define a Typescript interface for a function?

  9. 9

    How can I define the width of one element based on another unrelated element?

  10. 10

    How to define a Typescript object type based on existing object values?

  11. 11

    How can i define in xsd that a simple/complex type contain another XML?

  12. 12

    Typescript: Can I define an n-length tuple type?

  13. 13

    Can I define a Typescript function parameter to have a type of boolean or string?

  14. 14

    With AngularJS, how can I conditionally add a class based on an action?

  15. 15

    How can I conditionally run a suite of tests based on iOS device?

  16. 16

    How can I stop "property does not exist on type JQuery" syntax errors when using Typescript?

  17. 17

    How should I define the type of a promise call back in Typescript?

  18. 18

    How can I select an object type to deserialize using Json.Net based on property values in the data

  19. 19

    Can I define events in Typescript?

  20. 20

    How can I define a property type as being a list (list, set, array, collection) of string in my YAML Swagger definition

  21. 21

    How can I define a property type as being a list (list, set, array, collection) of string in my YAML Swagger definition

  22. 22

    How can I augment a typescript interface using a type exported from another .d.ts file?

  23. 23

    How Can I Create A Custom Property Type

  24. 24

    How can I declare the type of a class property?

  25. 25

    How can I solve "Cannot read property 'define' of undefined"?

  26. 26

    How can I define a property that returns the current value of a RadioButton in Kotlin?

  27. 27

    How to define an opaque type in TypeScript?

  28. 28

    How can I define a Typescript object return value for a function?

  29. 29

    How can I define an interface for an array of objects with Typescript?

HotTag

Archive