How is a function able to know the exact return value depending on whether the parameter is undefined or not?











up vote
1
down vote

favorite












interface Activity {
eat: () => void
}

interface Person {
activity?: Activity
}

const activity = <T extends Person>(person: T) => ({
eat: person.activity && person.activity.eat
})

const tom = {
activity: {
eat: () => {}
}
}

const tomAct = activity(tom)
tomAct.eat() // should know `eat` does exist

const bobAct = activity({})
bobAct.eat // should know `eat` is undefined


You can see tomAct.eat will return eat: (() => void) | undefined but tomAct in this case knowns that eat: (() => void and bobAct is known undefined.



Does Typescript support this case? How can I solve that?



===



"typescript": "^3.1.2",










share|improve this question




























    up vote
    1
    down vote

    favorite












    interface Activity {
    eat: () => void
    }

    interface Person {
    activity?: Activity
    }

    const activity = <T extends Person>(person: T) => ({
    eat: person.activity && person.activity.eat
    })

    const tom = {
    activity: {
    eat: () => {}
    }
    }

    const tomAct = activity(tom)
    tomAct.eat() // should know `eat` does exist

    const bobAct = activity({})
    bobAct.eat // should know `eat` is undefined


    You can see tomAct.eat will return eat: (() => void) | undefined but tomAct in this case knowns that eat: (() => void and bobAct is known undefined.



    Does Typescript support this case? How can I solve that?



    ===



    "typescript": "^3.1.2",










    share|improve this question


























      up vote
      1
      down vote

      favorite









      up vote
      1
      down vote

      favorite











      interface Activity {
      eat: () => void
      }

      interface Person {
      activity?: Activity
      }

      const activity = <T extends Person>(person: T) => ({
      eat: person.activity && person.activity.eat
      })

      const tom = {
      activity: {
      eat: () => {}
      }
      }

      const tomAct = activity(tom)
      tomAct.eat() // should know `eat` does exist

      const bobAct = activity({})
      bobAct.eat // should know `eat` is undefined


      You can see tomAct.eat will return eat: (() => void) | undefined but tomAct in this case knowns that eat: (() => void and bobAct is known undefined.



      Does Typescript support this case? How can I solve that?



      ===



      "typescript": "^3.1.2",










      share|improve this question















      interface Activity {
      eat: () => void
      }

      interface Person {
      activity?: Activity
      }

      const activity = <T extends Person>(person: T) => ({
      eat: person.activity && person.activity.eat
      })

      const tom = {
      activity: {
      eat: () => {}
      }
      }

      const tomAct = activity(tom)
      tomAct.eat() // should know `eat` does exist

      const bobAct = activity({})
      bobAct.eat // should know `eat` is undefined


      You can see tomAct.eat will return eat: (() => void) | undefined but tomAct in this case knowns that eat: (() => void and bobAct is known undefined.



      Does Typescript support this case? How can I solve that?



      ===



      "typescript": "^3.1.2",







      typescript






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited Nov 9 at 9:05









      marc_s

      566k12610921245




      566k12610921245










      asked Nov 9 at 8:06









      Le Tom

      649




      649
























          2 Answers
          2






          active

          oldest

          votes

















          up vote
          1
          down vote



          accepted










          Your problem is that control flow analysis doesn't really work very well on generics. The compiler is essentially widening T to Person for the purposes of control flow analysis (figuring out what type person.activity && person.activity.eat will be), so the inferred return type of activity() is the same as that of the concrete (non-generic) version of the function:



          const activityConcrete = (person: Person) => ({
          eat: person.activity && person.activity.eat
          }); // {eat: ()=>void | undefined}


          In order to get the behavior you want you either need to walk the compiler through the analysis (which is sometimes impossible) or just assert the return type you expect. Traditionally what you'd do here is to use overloads to represent the relationship between input and output types:



          function activity(person: { activity: Activity }): Activity;
          function activity(person: { activity?: undefined }): { eat: undefined };
          function activity(person: Person): { eat: Activity['eat'] | undefined };
          function activity(person: Person): { eat: Activity['eat'] | undefined } {
          return {
          eat: person.activity && person.activity.eat
          }
          }


          As of TypeScript 2.8 you can use conditional types to represent the same thing:



          type PersonEat<T extends Person> = T['activity'] extends infer A ? 
          A extends Activity ? A['eat'] : undefined : never;

          const activity = <T extends Person>(person: T) => ({
          eat: person.activity && person.activity.eat
          } as { eat: PersonEat<T> })


          Either way should result in similar behavior:



          const tom = {
          activity: {
          eat: () => {}
          }
          }
          const bob = {};

          const tomAct = activity(tom)
          tomAct.eat() // okay 🙂

          const bobAct = activity(bob)
          bobAct.eat // undefined 🙂


          So that works.





          Please note that there's a bit of a wrinkle with how it treats a Person without an activity. The type of bob above is {}, which is treated as a top type for objects, meaning that it absorbs any other object type you union with it. That is, in:



          const tomOrBob = Math.random() < 0.5 ? tom : bob; // type is {}


          it is inferred that tomOrBob is of type {} | {activity: Activity}, which is collapsed to just {}. So the compiler forgets that tomOrBob might have an activity. And that leads to the following incorrect behavior:



          const tomOrBobActivity = activity(tomOrBob);
          tomOrBobActivity.eat; // undefined 🙁 but it should be (()=>void) | undefined


          If you're okay with that overzealous undefinedness, fine. Otherwise, you need to explicitly tell the compiler to remember that activity is missing from bob:



          const bob: { activity?: undefined } = {}; // bob definitely is missing activity

          const bobAct = activity(bob);
          bobAct.eat // still undefined as desired 🙂

          const tomOrBob = Math.random() < 0.5 ? tom : bob;

          const tomOrBobAct = activity(tomOrBob);
          tomOrBobAct.eat; // (() => void) | undefined 🙂


          And that behaves as desired.





          Okay, hope that helps. Good luck!






          share|improve this answer





















          • Thank you for the detailed and informative answer @jcal.
            – Le Tom
            Nov 12 at 2:52










          • One more question about definition PersonEat why you use never in the end, I though it will be undefined. Could you tell me what i'm wrong?
            – Le Tom
            Nov 12 at 3:06






          • 1




            The construction X extends infer Y ? Z : never uses conditional type inference to assign X to a new type variable Y. The conditional type X extends infer Y will always be satisfied, so the never is ignored. In the case of PersonEat, I wanted to make T['activity'] distribute across a union, but you need a "naked" type parameter like A, so I reassigned it.
            – jcalz
            Nov 12 at 3:45






          • 1




            If it's too confusing, I'd recommend staying with the overloads.
            – jcalz
            Nov 12 at 3:45


















          up vote
          1
          down vote













          Typescript is a transpiler that works on compile time, therefore it can know only the things that are known at that time.



          You requirement is runtime requirement, the value of some property will be known only at runtime, therefore it is not possible to do with TS.






          share|improve this answer





















            Your Answer






            StackExchange.ifUsing("editor", function () {
            StackExchange.using("externalEditor", function () {
            StackExchange.using("snippets", function () {
            StackExchange.snippets.init();
            });
            });
            }, "code-snippets");

            StackExchange.ready(function() {
            var channelOptions = {
            tags: "".split(" "),
            id: "1"
            };
            initTagRenderer("".split(" "), "".split(" "), channelOptions);

            StackExchange.using("externalEditor", function() {
            // Have to fire editor after snippets, if snippets enabled
            if (StackExchange.settings.snippets.snippetsEnabled) {
            StackExchange.using("snippets", function() {
            createEditor();
            });
            }
            else {
            createEditor();
            }
            });

            function createEditor() {
            StackExchange.prepareEditor({
            heartbeatType: 'answer',
            convertImagesToLinks: true,
            noModals: true,
            showLowRepImageUploadWarning: true,
            reputationToPostImages: 10,
            bindNavPrevention: true,
            postfix: "",
            imageUploader: {
            brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
            contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
            allowUrls: true
            },
            onDemand: true,
            discardSelector: ".discard-answer"
            ,immediatelyShowMarkdownHelp:true
            });


            }
            });














             

            draft saved


            draft discarded


















            StackExchange.ready(
            function () {
            StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53221898%2fhow-is-a-function-able-to-know-the-exact-return-value-depending-on-whether-the-p%23new-answer', 'question_page');
            }
            );

            Post as a guest















            Required, but never shown

























            2 Answers
            2






            active

            oldest

            votes








            2 Answers
            2






            active

            oldest

            votes









            active

            oldest

            votes






            active

            oldest

            votes








            up vote
            1
            down vote



            accepted










            Your problem is that control flow analysis doesn't really work very well on generics. The compiler is essentially widening T to Person for the purposes of control flow analysis (figuring out what type person.activity && person.activity.eat will be), so the inferred return type of activity() is the same as that of the concrete (non-generic) version of the function:



            const activityConcrete = (person: Person) => ({
            eat: person.activity && person.activity.eat
            }); // {eat: ()=>void | undefined}


            In order to get the behavior you want you either need to walk the compiler through the analysis (which is sometimes impossible) or just assert the return type you expect. Traditionally what you'd do here is to use overloads to represent the relationship between input and output types:



            function activity(person: { activity: Activity }): Activity;
            function activity(person: { activity?: undefined }): { eat: undefined };
            function activity(person: Person): { eat: Activity['eat'] | undefined };
            function activity(person: Person): { eat: Activity['eat'] | undefined } {
            return {
            eat: person.activity && person.activity.eat
            }
            }


            As of TypeScript 2.8 you can use conditional types to represent the same thing:



            type PersonEat<T extends Person> = T['activity'] extends infer A ? 
            A extends Activity ? A['eat'] : undefined : never;

            const activity = <T extends Person>(person: T) => ({
            eat: person.activity && person.activity.eat
            } as { eat: PersonEat<T> })


            Either way should result in similar behavior:



            const tom = {
            activity: {
            eat: () => {}
            }
            }
            const bob = {};

            const tomAct = activity(tom)
            tomAct.eat() // okay 🙂

            const bobAct = activity(bob)
            bobAct.eat // undefined 🙂


            So that works.





            Please note that there's a bit of a wrinkle with how it treats a Person without an activity. The type of bob above is {}, which is treated as a top type for objects, meaning that it absorbs any other object type you union with it. That is, in:



            const tomOrBob = Math.random() < 0.5 ? tom : bob; // type is {}


            it is inferred that tomOrBob is of type {} | {activity: Activity}, which is collapsed to just {}. So the compiler forgets that tomOrBob might have an activity. And that leads to the following incorrect behavior:



            const tomOrBobActivity = activity(tomOrBob);
            tomOrBobActivity.eat; // undefined 🙁 but it should be (()=>void) | undefined


            If you're okay with that overzealous undefinedness, fine. Otherwise, you need to explicitly tell the compiler to remember that activity is missing from bob:



            const bob: { activity?: undefined } = {}; // bob definitely is missing activity

            const bobAct = activity(bob);
            bobAct.eat // still undefined as desired 🙂

            const tomOrBob = Math.random() < 0.5 ? tom : bob;

            const tomOrBobAct = activity(tomOrBob);
            tomOrBobAct.eat; // (() => void) | undefined 🙂


            And that behaves as desired.





            Okay, hope that helps. Good luck!






            share|improve this answer





















            • Thank you for the detailed and informative answer @jcal.
              – Le Tom
              Nov 12 at 2:52










            • One more question about definition PersonEat why you use never in the end, I though it will be undefined. Could you tell me what i'm wrong?
              – Le Tom
              Nov 12 at 3:06






            • 1




              The construction X extends infer Y ? Z : never uses conditional type inference to assign X to a new type variable Y. The conditional type X extends infer Y will always be satisfied, so the never is ignored. In the case of PersonEat, I wanted to make T['activity'] distribute across a union, but you need a "naked" type parameter like A, so I reassigned it.
              – jcalz
              Nov 12 at 3:45






            • 1




              If it's too confusing, I'd recommend staying with the overloads.
              – jcalz
              Nov 12 at 3:45















            up vote
            1
            down vote



            accepted










            Your problem is that control flow analysis doesn't really work very well on generics. The compiler is essentially widening T to Person for the purposes of control flow analysis (figuring out what type person.activity && person.activity.eat will be), so the inferred return type of activity() is the same as that of the concrete (non-generic) version of the function:



            const activityConcrete = (person: Person) => ({
            eat: person.activity && person.activity.eat
            }); // {eat: ()=>void | undefined}


            In order to get the behavior you want you either need to walk the compiler through the analysis (which is sometimes impossible) or just assert the return type you expect. Traditionally what you'd do here is to use overloads to represent the relationship between input and output types:



            function activity(person: { activity: Activity }): Activity;
            function activity(person: { activity?: undefined }): { eat: undefined };
            function activity(person: Person): { eat: Activity['eat'] | undefined };
            function activity(person: Person): { eat: Activity['eat'] | undefined } {
            return {
            eat: person.activity && person.activity.eat
            }
            }


            As of TypeScript 2.8 you can use conditional types to represent the same thing:



            type PersonEat<T extends Person> = T['activity'] extends infer A ? 
            A extends Activity ? A['eat'] : undefined : never;

            const activity = <T extends Person>(person: T) => ({
            eat: person.activity && person.activity.eat
            } as { eat: PersonEat<T> })


            Either way should result in similar behavior:



            const tom = {
            activity: {
            eat: () => {}
            }
            }
            const bob = {};

            const tomAct = activity(tom)
            tomAct.eat() // okay 🙂

            const bobAct = activity(bob)
            bobAct.eat // undefined 🙂


            So that works.





            Please note that there's a bit of a wrinkle with how it treats a Person without an activity. The type of bob above is {}, which is treated as a top type for objects, meaning that it absorbs any other object type you union with it. That is, in:



            const tomOrBob = Math.random() < 0.5 ? tom : bob; // type is {}


            it is inferred that tomOrBob is of type {} | {activity: Activity}, which is collapsed to just {}. So the compiler forgets that tomOrBob might have an activity. And that leads to the following incorrect behavior:



            const tomOrBobActivity = activity(tomOrBob);
            tomOrBobActivity.eat; // undefined 🙁 but it should be (()=>void) | undefined


            If you're okay with that overzealous undefinedness, fine. Otherwise, you need to explicitly tell the compiler to remember that activity is missing from bob:



            const bob: { activity?: undefined } = {}; // bob definitely is missing activity

            const bobAct = activity(bob);
            bobAct.eat // still undefined as desired 🙂

            const tomOrBob = Math.random() < 0.5 ? tom : bob;

            const tomOrBobAct = activity(tomOrBob);
            tomOrBobAct.eat; // (() => void) | undefined 🙂


            And that behaves as desired.





            Okay, hope that helps. Good luck!






            share|improve this answer





















            • Thank you for the detailed and informative answer @jcal.
              – Le Tom
              Nov 12 at 2:52










            • One more question about definition PersonEat why you use never in the end, I though it will be undefined. Could you tell me what i'm wrong?
              – Le Tom
              Nov 12 at 3:06






            • 1




              The construction X extends infer Y ? Z : never uses conditional type inference to assign X to a new type variable Y. The conditional type X extends infer Y will always be satisfied, so the never is ignored. In the case of PersonEat, I wanted to make T['activity'] distribute across a union, but you need a "naked" type parameter like A, so I reassigned it.
              – jcalz
              Nov 12 at 3:45






            • 1




              If it's too confusing, I'd recommend staying with the overloads.
              – jcalz
              Nov 12 at 3:45













            up vote
            1
            down vote



            accepted







            up vote
            1
            down vote



            accepted






            Your problem is that control flow analysis doesn't really work very well on generics. The compiler is essentially widening T to Person for the purposes of control flow analysis (figuring out what type person.activity && person.activity.eat will be), so the inferred return type of activity() is the same as that of the concrete (non-generic) version of the function:



            const activityConcrete = (person: Person) => ({
            eat: person.activity && person.activity.eat
            }); // {eat: ()=>void | undefined}


            In order to get the behavior you want you either need to walk the compiler through the analysis (which is sometimes impossible) or just assert the return type you expect. Traditionally what you'd do here is to use overloads to represent the relationship between input and output types:



            function activity(person: { activity: Activity }): Activity;
            function activity(person: { activity?: undefined }): { eat: undefined };
            function activity(person: Person): { eat: Activity['eat'] | undefined };
            function activity(person: Person): { eat: Activity['eat'] | undefined } {
            return {
            eat: person.activity && person.activity.eat
            }
            }


            As of TypeScript 2.8 you can use conditional types to represent the same thing:



            type PersonEat<T extends Person> = T['activity'] extends infer A ? 
            A extends Activity ? A['eat'] : undefined : never;

            const activity = <T extends Person>(person: T) => ({
            eat: person.activity && person.activity.eat
            } as { eat: PersonEat<T> })


            Either way should result in similar behavior:



            const tom = {
            activity: {
            eat: () => {}
            }
            }
            const bob = {};

            const tomAct = activity(tom)
            tomAct.eat() // okay 🙂

            const bobAct = activity(bob)
            bobAct.eat // undefined 🙂


            So that works.





            Please note that there's a bit of a wrinkle with how it treats a Person without an activity. The type of bob above is {}, which is treated as a top type for objects, meaning that it absorbs any other object type you union with it. That is, in:



            const tomOrBob = Math.random() < 0.5 ? tom : bob; // type is {}


            it is inferred that tomOrBob is of type {} | {activity: Activity}, which is collapsed to just {}. So the compiler forgets that tomOrBob might have an activity. And that leads to the following incorrect behavior:



            const tomOrBobActivity = activity(tomOrBob);
            tomOrBobActivity.eat; // undefined 🙁 but it should be (()=>void) | undefined


            If you're okay with that overzealous undefinedness, fine. Otherwise, you need to explicitly tell the compiler to remember that activity is missing from bob:



            const bob: { activity?: undefined } = {}; // bob definitely is missing activity

            const bobAct = activity(bob);
            bobAct.eat // still undefined as desired 🙂

            const tomOrBob = Math.random() < 0.5 ? tom : bob;

            const tomOrBobAct = activity(tomOrBob);
            tomOrBobAct.eat; // (() => void) | undefined 🙂


            And that behaves as desired.





            Okay, hope that helps. Good luck!






            share|improve this answer












            Your problem is that control flow analysis doesn't really work very well on generics. The compiler is essentially widening T to Person for the purposes of control flow analysis (figuring out what type person.activity && person.activity.eat will be), so the inferred return type of activity() is the same as that of the concrete (non-generic) version of the function:



            const activityConcrete = (person: Person) => ({
            eat: person.activity && person.activity.eat
            }); // {eat: ()=>void | undefined}


            In order to get the behavior you want you either need to walk the compiler through the analysis (which is sometimes impossible) or just assert the return type you expect. Traditionally what you'd do here is to use overloads to represent the relationship between input and output types:



            function activity(person: { activity: Activity }): Activity;
            function activity(person: { activity?: undefined }): { eat: undefined };
            function activity(person: Person): { eat: Activity['eat'] | undefined };
            function activity(person: Person): { eat: Activity['eat'] | undefined } {
            return {
            eat: person.activity && person.activity.eat
            }
            }


            As of TypeScript 2.8 you can use conditional types to represent the same thing:



            type PersonEat<T extends Person> = T['activity'] extends infer A ? 
            A extends Activity ? A['eat'] : undefined : never;

            const activity = <T extends Person>(person: T) => ({
            eat: person.activity && person.activity.eat
            } as { eat: PersonEat<T> })


            Either way should result in similar behavior:



            const tom = {
            activity: {
            eat: () => {}
            }
            }
            const bob = {};

            const tomAct = activity(tom)
            tomAct.eat() // okay 🙂

            const bobAct = activity(bob)
            bobAct.eat // undefined 🙂


            So that works.





            Please note that there's a bit of a wrinkle with how it treats a Person without an activity. The type of bob above is {}, which is treated as a top type for objects, meaning that it absorbs any other object type you union with it. That is, in:



            const tomOrBob = Math.random() < 0.5 ? tom : bob; // type is {}


            it is inferred that tomOrBob is of type {} | {activity: Activity}, which is collapsed to just {}. So the compiler forgets that tomOrBob might have an activity. And that leads to the following incorrect behavior:



            const tomOrBobActivity = activity(tomOrBob);
            tomOrBobActivity.eat; // undefined 🙁 but it should be (()=>void) | undefined


            If you're okay with that overzealous undefinedness, fine. Otherwise, you need to explicitly tell the compiler to remember that activity is missing from bob:



            const bob: { activity?: undefined } = {}; // bob definitely is missing activity

            const bobAct = activity(bob);
            bobAct.eat // still undefined as desired 🙂

            const tomOrBob = Math.random() < 0.5 ? tom : bob;

            const tomOrBobAct = activity(tomOrBob);
            tomOrBobAct.eat; // (() => void) | undefined 🙂


            And that behaves as desired.





            Okay, hope that helps. Good luck!







            share|improve this answer












            share|improve this answer



            share|improve this answer










            answered Nov 9 at 15:09









            jcalz

            20.4k21535




            20.4k21535












            • Thank you for the detailed and informative answer @jcal.
              – Le Tom
              Nov 12 at 2:52










            • One more question about definition PersonEat why you use never in the end, I though it will be undefined. Could you tell me what i'm wrong?
              – Le Tom
              Nov 12 at 3:06






            • 1




              The construction X extends infer Y ? Z : never uses conditional type inference to assign X to a new type variable Y. The conditional type X extends infer Y will always be satisfied, so the never is ignored. In the case of PersonEat, I wanted to make T['activity'] distribute across a union, but you need a "naked" type parameter like A, so I reassigned it.
              – jcalz
              Nov 12 at 3:45






            • 1




              If it's too confusing, I'd recommend staying with the overloads.
              – jcalz
              Nov 12 at 3:45


















            • Thank you for the detailed and informative answer @jcal.
              – Le Tom
              Nov 12 at 2:52










            • One more question about definition PersonEat why you use never in the end, I though it will be undefined. Could you tell me what i'm wrong?
              – Le Tom
              Nov 12 at 3:06






            • 1




              The construction X extends infer Y ? Z : never uses conditional type inference to assign X to a new type variable Y. The conditional type X extends infer Y will always be satisfied, so the never is ignored. In the case of PersonEat, I wanted to make T['activity'] distribute across a union, but you need a "naked" type parameter like A, so I reassigned it.
              – jcalz
              Nov 12 at 3:45






            • 1




              If it's too confusing, I'd recommend staying with the overloads.
              – jcalz
              Nov 12 at 3:45
















            Thank you for the detailed and informative answer @jcal.
            – Le Tom
            Nov 12 at 2:52




            Thank you for the detailed and informative answer @jcal.
            – Le Tom
            Nov 12 at 2:52












            One more question about definition PersonEat why you use never in the end, I though it will be undefined. Could you tell me what i'm wrong?
            – Le Tom
            Nov 12 at 3:06




            One more question about definition PersonEat why you use never in the end, I though it will be undefined. Could you tell me what i'm wrong?
            – Le Tom
            Nov 12 at 3:06




            1




            1




            The construction X extends infer Y ? Z : never uses conditional type inference to assign X to a new type variable Y. The conditional type X extends infer Y will always be satisfied, so the never is ignored. In the case of PersonEat, I wanted to make T['activity'] distribute across a union, but you need a "naked" type parameter like A, so I reassigned it.
            – jcalz
            Nov 12 at 3:45




            The construction X extends infer Y ? Z : never uses conditional type inference to assign X to a new type variable Y. The conditional type X extends infer Y will always be satisfied, so the never is ignored. In the case of PersonEat, I wanted to make T['activity'] distribute across a union, but you need a "naked" type parameter like A, so I reassigned it.
            – jcalz
            Nov 12 at 3:45




            1




            1




            If it's too confusing, I'd recommend staying with the overloads.
            – jcalz
            Nov 12 at 3:45




            If it's too confusing, I'd recommend staying with the overloads.
            – jcalz
            Nov 12 at 3:45












            up vote
            1
            down vote













            Typescript is a transpiler that works on compile time, therefore it can know only the things that are known at that time.



            You requirement is runtime requirement, the value of some property will be known only at runtime, therefore it is not possible to do with TS.






            share|improve this answer

























              up vote
              1
              down vote













              Typescript is a transpiler that works on compile time, therefore it can know only the things that are known at that time.



              You requirement is runtime requirement, the value of some property will be known only at runtime, therefore it is not possible to do with TS.






              share|improve this answer























                up vote
                1
                down vote










                up vote
                1
                down vote









                Typescript is a transpiler that works on compile time, therefore it can know only the things that are known at that time.



                You requirement is runtime requirement, the value of some property will be known only at runtime, therefore it is not possible to do with TS.






                share|improve this answer












                Typescript is a transpiler that works on compile time, therefore it can know only the things that are known at that time.



                You requirement is runtime requirement, the value of some property will be known only at runtime, therefore it is not possible to do with TS.







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered Nov 9 at 9:36









                felixmosh

                3,6722517




                3,6722517






























                     

                    draft saved


                    draft discarded



















































                     


                    draft saved


                    draft discarded














                    StackExchange.ready(
                    function () {
                    StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53221898%2fhow-is-a-function-able-to-know-the-exact-return-value-depending-on-whether-the-p%23new-answer', 'question_page');
                    }
                    );

                    Post as a guest















                    Required, but never shown





















































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown

































                    Required, but never shown














                    Required, but never shown












                    Required, but never shown







                    Required, but never shown







                    Popular posts from this blog

                    Schultheiß

                    Verwaltungsgliederung Dänemarks

                    Liste der Kulturdenkmale in Wilsdruff