Why Typescript interfaces allow self references?
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
add a comment |
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
How else do you suggest implementing a “cloneable” interface, say?
– Konrad Rudolph
Nov 13 '18 at 10:51
add a comment |
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
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
typescript types
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
add a comment |
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
add a comment |
2 Answers
2
active
oldest
votes
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; ;
add a comment |
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.
add a comment |
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
);
);
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
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; ;
add a comment |
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; ;
add a comment |
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; ;
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; ;
edited Nov 13 '18 at 11:10
answered Nov 13 '18 at 10:53
Titian Cernicova-DragomirTitian Cernicova-Dragomir
64k34159
64k34159
add a comment |
add a comment |
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.
add a comment |
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.
add a comment |
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.
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.
answered Nov 13 '18 at 10:51
Murat KaragözMurat Karagöz
14.6k53568
14.6k53568
add a comment |
add a comment |
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.
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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
How else do you suggest implementing a “cloneable” interface, say?
– Konrad Rudolph
Nov 13 '18 at 10:51