Why Typescript interfaces allow self references?










3















type SomeGeneric<T> = 
x: T;


type TTest = SomeGeneric<TTest> & a: string; ; // Type alias 'TTest' circularly references itself.

interface ITest extends SomeGeneric<ITest> // OK
a: string;



I fail to understand why interfaces are allowed to reference themselves in their own declarations and types aren't.










share|improve this question






















  • How else do you suggest implementing a “cloneable” interface, say?

    – Konrad Rudolph
    Nov 13 '18 at 10:51
















3















type SomeGeneric<T> = 
x: T;


type TTest = SomeGeneric<TTest> & a: string; ; // Type alias 'TTest' circularly references itself.

interface ITest extends SomeGeneric<ITest> // OK
a: string;



I fail to understand why interfaces are allowed to reference themselves in their own declarations and types aren't.










share|improve this question






















  • How else do you suggest implementing a “cloneable” interface, say?

    – Konrad Rudolph
    Nov 13 '18 at 10:51














3












3








3








type SomeGeneric<T> = 
x: T;


type TTest = SomeGeneric<TTest> & a: string; ; // Type alias 'TTest' circularly references itself.

interface ITest extends SomeGeneric<ITest> // OK
a: string;



I fail to understand why interfaces are allowed to reference themselves in their own declarations and types aren't.










share|improve this question














type SomeGeneric<T> = 
x: T;


type TTest = SomeGeneric<TTest> & a: string; ; // Type alias 'TTest' circularly references itself.

interface ITest extends SomeGeneric<ITest> // OK
a: string;



I fail to understand why interfaces are allowed to reference themselves in their own declarations and types aren't.







typescript types






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Nov 13 '18 at 10:46









Andrey GodyaevAndrey Godyaev

12417




12417












  • How else do you suggest implementing a “cloneable” interface, say?

    – Konrad Rudolph
    Nov 13 '18 at 10:51


















  • How else do you suggest implementing a “cloneable” interface, say?

    – Konrad Rudolph
    Nov 13 '18 at 10:51

















How else do you suggest implementing a “cloneable” interface, say?

– Konrad Rudolph
Nov 13 '18 at 10:51






How else do you suggest implementing a “cloneable” interface, say?

– Konrad Rudolph
Nov 13 '18 at 10:51













2 Answers
2






active

oldest

votes


















3














Type aliases can't be recursive (mostly), while interfaces can. If you search GitHub there are several answers given as to why.



For example RyanCavanaugh explains here:




Type aliases and interfaces are subtly different and there are rules about self-recursion for aliases that don't apply to interfaces. Because an alias is supposed to always be "immediately expandable" (it's as if it were an in-place expansion of its referand), there are things you can do interfaces you can't do with type aliases.




Or here by DanielRosenwasser




The restriction that a type alias can't be referenced by itself at the top level has been the behavior since we implemented type aliases; however, you might recall that a while back, we started allowing type aliases to be referenced from within an object type.
...
However, it has no problem provided that the type can be expanded, or "unrolled" one level at a time. In other words, as long as you have some sort of box-y thing containing the type:




The general workaround is to use an interface if you want a recursive type.



Just as a side note, the rules are not exactly enforced consistently. For example while in your case SomeGeneric<TTest> could be expanded to x: TTest the compiler does not even attempt it, it just fails. The following recursive definition will however work:



type TTest = x: TTest & a: string; ;





share|improve this answer
































    1














    The reason why it works for extends is because you basically extend the interface by one more field and that is the interface itself. e.g.



    private x: ITest; // is fine
    private a: string;


    By trying to declare a type referencing itself will result in an endless reference loop. That's why it does not work.






    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',
      autoActivateHeartbeat: false,
      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%2f53279304%2fwhy-typescript-interfaces-allow-self-references%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









      3














      Type aliases can't be recursive (mostly), while interfaces can. If you search GitHub there are several answers given as to why.



      For example RyanCavanaugh explains here:




      Type aliases and interfaces are subtly different and there are rules about self-recursion for aliases that don't apply to interfaces. Because an alias is supposed to always be "immediately expandable" (it's as if it were an in-place expansion of its referand), there are things you can do interfaces you can't do with type aliases.




      Or here by DanielRosenwasser




      The restriction that a type alias can't be referenced by itself at the top level has been the behavior since we implemented type aliases; however, you might recall that a while back, we started allowing type aliases to be referenced from within an object type.
      ...
      However, it has no problem provided that the type can be expanded, or "unrolled" one level at a time. In other words, as long as you have some sort of box-y thing containing the type:




      The general workaround is to use an interface if you want a recursive type.



      Just as a side note, the rules are not exactly enforced consistently. For example while in your case SomeGeneric<TTest> could be expanded to x: TTest the compiler does not even attempt it, it just fails. The following recursive definition will however work:



      type TTest = x: TTest & a: string; ;





      share|improve this answer





























        3














        Type aliases can't be recursive (mostly), while interfaces can. If you search GitHub there are several answers given as to why.



        For example RyanCavanaugh explains here:




        Type aliases and interfaces are subtly different and there are rules about self-recursion for aliases that don't apply to interfaces. Because an alias is supposed to always be "immediately expandable" (it's as if it were an in-place expansion of its referand), there are things you can do interfaces you can't do with type aliases.




        Or here by DanielRosenwasser




        The restriction that a type alias can't be referenced by itself at the top level has been the behavior since we implemented type aliases; however, you might recall that a while back, we started allowing type aliases to be referenced from within an object type.
        ...
        However, it has no problem provided that the type can be expanded, or "unrolled" one level at a time. In other words, as long as you have some sort of box-y thing containing the type:




        The general workaround is to use an interface if you want a recursive type.



        Just as a side note, the rules are not exactly enforced consistently. For example while in your case SomeGeneric<TTest> could be expanded to x: TTest the compiler does not even attempt it, it just fails. The following recursive definition will however work:



        type TTest = x: TTest & a: string; ;





        share|improve this answer



























          3












          3








          3







          Type aliases can't be recursive (mostly), while interfaces can. If you search GitHub there are several answers given as to why.



          For example RyanCavanaugh explains here:




          Type aliases and interfaces are subtly different and there are rules about self-recursion for aliases that don't apply to interfaces. Because an alias is supposed to always be "immediately expandable" (it's as if it were an in-place expansion of its referand), there are things you can do interfaces you can't do with type aliases.




          Or here by DanielRosenwasser




          The restriction that a type alias can't be referenced by itself at the top level has been the behavior since we implemented type aliases; however, you might recall that a while back, we started allowing type aliases to be referenced from within an object type.
          ...
          However, it has no problem provided that the type can be expanded, or "unrolled" one level at a time. In other words, as long as you have some sort of box-y thing containing the type:




          The general workaround is to use an interface if you want a recursive type.



          Just as a side note, the rules are not exactly enforced consistently. For example while in your case SomeGeneric<TTest> could be expanded to x: TTest the compiler does not even attempt it, it just fails. The following recursive definition will however work:



          type TTest = x: TTest & a: string; ;





          share|improve this answer















          Type aliases can't be recursive (mostly), while interfaces can. If you search GitHub there are several answers given as to why.



          For example RyanCavanaugh explains here:




          Type aliases and interfaces are subtly different and there are rules about self-recursion for aliases that don't apply to interfaces. Because an alias is supposed to always be "immediately expandable" (it's as if it were an in-place expansion of its referand), there are things you can do interfaces you can't do with type aliases.




          Or here by DanielRosenwasser




          The restriction that a type alias can't be referenced by itself at the top level has been the behavior since we implemented type aliases; however, you might recall that a while back, we started allowing type aliases to be referenced from within an object type.
          ...
          However, it has no problem provided that the type can be expanded, or "unrolled" one level at a time. In other words, as long as you have some sort of box-y thing containing the type:




          The general workaround is to use an interface if you want a recursive type.



          Just as a side note, the rules are not exactly enforced consistently. For example while in your case SomeGeneric<TTest> could be expanded to x: TTest the compiler does not even attempt it, it just fails. The following recursive definition will however work:



          type TTest = x: TTest & a: string; ;






          share|improve this answer














          share|improve this answer



          share|improve this answer








          edited Nov 13 '18 at 11:10

























          answered Nov 13 '18 at 10:53









          Titian Cernicova-DragomirTitian Cernicova-Dragomir

          64k34159




          64k34159























              1














              The reason why it works for extends is because you basically extend the interface by one more field and that is the interface itself. e.g.



              private x: ITest; // is fine
              private a: string;


              By trying to declare a type referencing itself will result in an endless reference loop. That's why it does not work.






              share|improve this answer



























                1














                The reason why it works for extends is because you basically extend the interface by one more field and that is the interface itself. e.g.



                private x: ITest; // is fine
                private a: string;


                By trying to declare a type referencing itself will result in an endless reference loop. That's why it does not work.






                share|improve this answer

























                  1












                  1








                  1







                  The reason why it works for extends is because you basically extend the interface by one more field and that is the interface itself. e.g.



                  private x: ITest; // is fine
                  private a: string;


                  By trying to declare a type referencing itself will result in an endless reference loop. That's why it does not work.






                  share|improve this answer













                  The reason why it works for extends is because you basically extend the interface by one more field and that is the interface itself. e.g.



                  private x: ITest; // is fine
                  private a: string;


                  By trying to declare a type referencing itself will result in an endless reference loop. That's why it does not work.







                  share|improve this answer












                  share|improve this answer



                  share|improve this answer










                  answered Nov 13 '18 at 10:51









                  Murat KaragözMurat Karagöz

                  14.6k53568




                  14.6k53568



























                      draft saved

                      draft discarded
















































                      Thanks for contributing an answer to Stack Overflow!


                      • Please be sure to answer the question. Provide details and share your research!

                      But avoid


                      • Asking for help, clarification, or responding to other answers.

                      • Making statements based on opinion; back them up with references or personal experience.

                      To learn more, see our tips on writing great answers.




                      draft saved


                      draft discarded














                      StackExchange.ready(
                      function ()
                      StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fstackoverflow.com%2fquestions%2f53279304%2fwhy-typescript-interfaces-allow-self-references%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

                      How to how show current date and time by default on contact form 7 in WordPress without taking input from user in datetimepicker

                      Syphilis

                      Darth Vader #20