Why doesn't Object.keys return a keyof type in TypeScript?2019 Community Moderator ElectionAvoid implicit...

Didactic impediments of using simplified versions

Is there a math equivalent to the conditional ternary operator?

It took me a lot of time to make this, pls like. (YouTube Comments #1)

I encountered my boss during an on-site interview at another company. Should I bring it up when seeing him next time?

What type of postprocessing gives the effect of people standing out

Is there a full canon version of Tyrion's jackass/honeycomb joke?

How to kill a localhost:8080

What are all the squawk codes?

When was drinking water recognized as crucial in marathon running?

Where is the fallacy here?

VAT refund for a conference ticket in Sweden

Six real numbers so that product of any five is the sixth one

How to lift/raise/repair a segment of concrete slab?

Are small insurances worth it

How to evaluate the limit where something is raised to a power of x?

How do you say "powers of ten"?

Can I become debt free or should I file for bankruptcy? How do I manage my debt and finances?

A right or the right?

Pure Functions: Does "No Side Effects" Imply "Always Same Output, Given Same Input"?

Reason why dimensional travelling would be restricted

Does an unattuned Frost Brand weapon still glow in freezing temperatures?

Non-Italian European mafias in USA?

How can I be pwned if I'm not registered on the compromised site?

What is a term for a function that when called repeatedly, has the same effect as calling once?



Why doesn't Object.keys return a keyof type in TypeScript?



2019 Community Moderator ElectionAvoid implicit 'any' type when using Object.keys in TypescriptWhat is TypeScript and why would I use it in place of JavaScript?Type definition in object literal in TypeScriptAre strongly-typed functions as parameters possible in TypeScript?is return-type signature in function overloading useless?Referencing TypeScript Type DefinitionsTypescript: Interfaces vs TypesWhy did TypeScript 2.0 change IteratorResult<K>?Typescript - type to represent any class?How to overwrite incorrect TypeScript type definition installed via @types/packagetypescript compiler bug? knockout.validation.d.ts doesn't compile anymore












8















Title says it all - why doesn't Object.keys(x) in TypeScript return the type Array<keyof typeof x>? That's what Object.keys does, so it seems like an obvious oversight on the part of the TypeScript definition file authors to not make the return type simply be keyof T.



Should I log a bug on their GitHub repo, or just go ahead and send a PR to fix it for them?










share|improve this question



























    8















    Title says it all - why doesn't Object.keys(x) in TypeScript return the type Array<keyof typeof x>? That's what Object.keys does, so it seems like an obvious oversight on the part of the TypeScript definition file authors to not make the return type simply be keyof T.



    Should I log a bug on their GitHub repo, or just go ahead and send a PR to fix it for them?










    share|improve this question

























      8












      8








      8








      Title says it all - why doesn't Object.keys(x) in TypeScript return the type Array<keyof typeof x>? That's what Object.keys does, so it seems like an obvious oversight on the part of the TypeScript definition file authors to not make the return type simply be keyof T.



      Should I log a bug on their GitHub repo, or just go ahead and send a PR to fix it for them?










      share|improve this question














      Title says it all - why doesn't Object.keys(x) in TypeScript return the type Array<keyof typeof x>? That's what Object.keys does, so it seems like an obvious oversight on the part of the TypeScript definition file authors to not make the return type simply be keyof T.



      Should I log a bug on their GitHub repo, or just go ahead and send a PR to fix it for them?







      typescript






      share|improve this question













      share|improve this question











      share|improve this question




      share|improve this question










      asked 1 hour ago









      Ryan CavanaughRyan Cavanaugh

      100k27171180




      100k27171180
























          2 Answers
          2






          active

          oldest

          votes


















          9














          The current return type (string[]) is intentional. Why?



          Consider some type like this:



          interface Point {
          x: number;
          y: number;
          }


          You write some code like this:



          function fn(k: keyof Point) {
          if (k === "x") {
          console.log("X axis");
          } else if (k === "y") {
          console.log("Y axis");
          } else {
          throw new Error("This is impossible");
          }
          }


          Let's ask a question:




          In a well-typed program, can a legal call to fn hit the error case?




          The desired answer is, of course, "No". But what does this have to do with Object.keys?



          Now consider this other code:



          interface NamedPoint extends Point {
          name: string;
          }

          const origin: NamedPoint = { name: "origin", x: 0, y: 0 };


          Note that according to TypeScript's type system, all NamedPoints are valid Points.



          Now let's write a little more code:



          function doSomething(pt: Point) {
          for (const k of Object.keys(pt)) {
          // A valid call iff Object.keys(pt) returns (keyof Point)[]
          fn(k);
          }
          }
          // Throws an exception
          doSomething(origin);


          Our well-typed program just threw an exception!



          Something went wrong here!
          By returning keyof T from Object.keys, we've violated the assumption that keyof T forms an exhaustive list, because having a reference to an object doesn't mean that the type of the reference isn't a supertype of the type of the value.



          Basically, (at least) one of the following four things can't be true:





          1. keyof T is an exhaustive list of the keys of T

          2. A type with additional properties is always a subtype of its base type

          3. It is legal to alias a subtype value by a supertype reference


          4. Object.keys returns keyof T


          Throwing away point 1 makes keyof nearly useless, because it implies that keyof Point might be some value that isn't "x" or "y".



          Throwing away point 2 completely destroys TypeScript's type system. Not an option.



          Throwing away point 3 also completely destroys TypeScript's type system.



          Throwing away point 4 is fine and makes you, the programmer, think about whether or not the object you're dealing with is possibly an alias for a subtype of the thing you think you have.



          The "missing feature" to make this legal but not contradictory is Exact Types, which would allow you to declare a new kind of type that wasn't subject to point #2. If this feature existed, it would presumably be possible to make Object.keys return keyof T only for Ts which were declared as exact.






          share|improve this answer


























          • However, there is common case when point 3. is excluded, when for example T is inferred and is guaranteed to be precise: const f: <T>(t: T) => void = (t) => { Object.keys(t).forEach(k => t[k]) }. I have lots of places like that in my code, where I really want Object.keys() to return (keyof T)[].

            – artem
            1 hour ago





















          0














          I opened and closed a PR today related to this topic.
          My PR was only focusing on the case where keys are coming from an enum of strings.



          In this precise case, it does not seem that inheritance is feasible.



          I need to double check before reopening it https://github.com/Microsoft/TypeScript/pull/30228






          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%2f55012174%2fwhy-doesnt-object-keys-return-a-keyof-type-in-typescript%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









            9














            The current return type (string[]) is intentional. Why?



            Consider some type like this:



            interface Point {
            x: number;
            y: number;
            }


            You write some code like this:



            function fn(k: keyof Point) {
            if (k === "x") {
            console.log("X axis");
            } else if (k === "y") {
            console.log("Y axis");
            } else {
            throw new Error("This is impossible");
            }
            }


            Let's ask a question:




            In a well-typed program, can a legal call to fn hit the error case?




            The desired answer is, of course, "No". But what does this have to do with Object.keys?



            Now consider this other code:



            interface NamedPoint extends Point {
            name: string;
            }

            const origin: NamedPoint = { name: "origin", x: 0, y: 0 };


            Note that according to TypeScript's type system, all NamedPoints are valid Points.



            Now let's write a little more code:



            function doSomething(pt: Point) {
            for (const k of Object.keys(pt)) {
            // A valid call iff Object.keys(pt) returns (keyof Point)[]
            fn(k);
            }
            }
            // Throws an exception
            doSomething(origin);


            Our well-typed program just threw an exception!



            Something went wrong here!
            By returning keyof T from Object.keys, we've violated the assumption that keyof T forms an exhaustive list, because having a reference to an object doesn't mean that the type of the reference isn't a supertype of the type of the value.



            Basically, (at least) one of the following four things can't be true:





            1. keyof T is an exhaustive list of the keys of T

            2. A type with additional properties is always a subtype of its base type

            3. It is legal to alias a subtype value by a supertype reference


            4. Object.keys returns keyof T


            Throwing away point 1 makes keyof nearly useless, because it implies that keyof Point might be some value that isn't "x" or "y".



            Throwing away point 2 completely destroys TypeScript's type system. Not an option.



            Throwing away point 3 also completely destroys TypeScript's type system.



            Throwing away point 4 is fine and makes you, the programmer, think about whether or not the object you're dealing with is possibly an alias for a subtype of the thing you think you have.



            The "missing feature" to make this legal but not contradictory is Exact Types, which would allow you to declare a new kind of type that wasn't subject to point #2. If this feature existed, it would presumably be possible to make Object.keys return keyof T only for Ts which were declared as exact.






            share|improve this answer


























            • However, there is common case when point 3. is excluded, when for example T is inferred and is guaranteed to be precise: const f: <T>(t: T) => void = (t) => { Object.keys(t).forEach(k => t[k]) }. I have lots of places like that in my code, where I really want Object.keys() to return (keyof T)[].

              – artem
              1 hour ago


















            9














            The current return type (string[]) is intentional. Why?



            Consider some type like this:



            interface Point {
            x: number;
            y: number;
            }


            You write some code like this:



            function fn(k: keyof Point) {
            if (k === "x") {
            console.log("X axis");
            } else if (k === "y") {
            console.log("Y axis");
            } else {
            throw new Error("This is impossible");
            }
            }


            Let's ask a question:




            In a well-typed program, can a legal call to fn hit the error case?




            The desired answer is, of course, "No". But what does this have to do with Object.keys?



            Now consider this other code:



            interface NamedPoint extends Point {
            name: string;
            }

            const origin: NamedPoint = { name: "origin", x: 0, y: 0 };


            Note that according to TypeScript's type system, all NamedPoints are valid Points.



            Now let's write a little more code:



            function doSomething(pt: Point) {
            for (const k of Object.keys(pt)) {
            // A valid call iff Object.keys(pt) returns (keyof Point)[]
            fn(k);
            }
            }
            // Throws an exception
            doSomething(origin);


            Our well-typed program just threw an exception!



            Something went wrong here!
            By returning keyof T from Object.keys, we've violated the assumption that keyof T forms an exhaustive list, because having a reference to an object doesn't mean that the type of the reference isn't a supertype of the type of the value.



            Basically, (at least) one of the following four things can't be true:





            1. keyof T is an exhaustive list of the keys of T

            2. A type with additional properties is always a subtype of its base type

            3. It is legal to alias a subtype value by a supertype reference


            4. Object.keys returns keyof T


            Throwing away point 1 makes keyof nearly useless, because it implies that keyof Point might be some value that isn't "x" or "y".



            Throwing away point 2 completely destroys TypeScript's type system. Not an option.



            Throwing away point 3 also completely destroys TypeScript's type system.



            Throwing away point 4 is fine and makes you, the programmer, think about whether or not the object you're dealing with is possibly an alias for a subtype of the thing you think you have.



            The "missing feature" to make this legal but not contradictory is Exact Types, which would allow you to declare a new kind of type that wasn't subject to point #2. If this feature existed, it would presumably be possible to make Object.keys return keyof T only for Ts which were declared as exact.






            share|improve this answer


























            • However, there is common case when point 3. is excluded, when for example T is inferred and is guaranteed to be precise: const f: <T>(t: T) => void = (t) => { Object.keys(t).forEach(k => t[k]) }. I have lots of places like that in my code, where I really want Object.keys() to return (keyof T)[].

              – artem
              1 hour ago
















            9












            9








            9







            The current return type (string[]) is intentional. Why?



            Consider some type like this:



            interface Point {
            x: number;
            y: number;
            }


            You write some code like this:



            function fn(k: keyof Point) {
            if (k === "x") {
            console.log("X axis");
            } else if (k === "y") {
            console.log("Y axis");
            } else {
            throw new Error("This is impossible");
            }
            }


            Let's ask a question:




            In a well-typed program, can a legal call to fn hit the error case?




            The desired answer is, of course, "No". But what does this have to do with Object.keys?



            Now consider this other code:



            interface NamedPoint extends Point {
            name: string;
            }

            const origin: NamedPoint = { name: "origin", x: 0, y: 0 };


            Note that according to TypeScript's type system, all NamedPoints are valid Points.



            Now let's write a little more code:



            function doSomething(pt: Point) {
            for (const k of Object.keys(pt)) {
            // A valid call iff Object.keys(pt) returns (keyof Point)[]
            fn(k);
            }
            }
            // Throws an exception
            doSomething(origin);


            Our well-typed program just threw an exception!



            Something went wrong here!
            By returning keyof T from Object.keys, we've violated the assumption that keyof T forms an exhaustive list, because having a reference to an object doesn't mean that the type of the reference isn't a supertype of the type of the value.



            Basically, (at least) one of the following four things can't be true:





            1. keyof T is an exhaustive list of the keys of T

            2. A type with additional properties is always a subtype of its base type

            3. It is legal to alias a subtype value by a supertype reference


            4. Object.keys returns keyof T


            Throwing away point 1 makes keyof nearly useless, because it implies that keyof Point might be some value that isn't "x" or "y".



            Throwing away point 2 completely destroys TypeScript's type system. Not an option.



            Throwing away point 3 also completely destroys TypeScript's type system.



            Throwing away point 4 is fine and makes you, the programmer, think about whether or not the object you're dealing with is possibly an alias for a subtype of the thing you think you have.



            The "missing feature" to make this legal but not contradictory is Exact Types, which would allow you to declare a new kind of type that wasn't subject to point #2. If this feature existed, it would presumably be possible to make Object.keys return keyof T only for Ts which were declared as exact.






            share|improve this answer















            The current return type (string[]) is intentional. Why?



            Consider some type like this:



            interface Point {
            x: number;
            y: number;
            }


            You write some code like this:



            function fn(k: keyof Point) {
            if (k === "x") {
            console.log("X axis");
            } else if (k === "y") {
            console.log("Y axis");
            } else {
            throw new Error("This is impossible");
            }
            }


            Let's ask a question:




            In a well-typed program, can a legal call to fn hit the error case?




            The desired answer is, of course, "No". But what does this have to do with Object.keys?



            Now consider this other code:



            interface NamedPoint extends Point {
            name: string;
            }

            const origin: NamedPoint = { name: "origin", x: 0, y: 0 };


            Note that according to TypeScript's type system, all NamedPoints are valid Points.



            Now let's write a little more code:



            function doSomething(pt: Point) {
            for (const k of Object.keys(pt)) {
            // A valid call iff Object.keys(pt) returns (keyof Point)[]
            fn(k);
            }
            }
            // Throws an exception
            doSomething(origin);


            Our well-typed program just threw an exception!



            Something went wrong here!
            By returning keyof T from Object.keys, we've violated the assumption that keyof T forms an exhaustive list, because having a reference to an object doesn't mean that the type of the reference isn't a supertype of the type of the value.



            Basically, (at least) one of the following four things can't be true:





            1. keyof T is an exhaustive list of the keys of T

            2. A type with additional properties is always a subtype of its base type

            3. It is legal to alias a subtype value by a supertype reference


            4. Object.keys returns keyof T


            Throwing away point 1 makes keyof nearly useless, because it implies that keyof Point might be some value that isn't "x" or "y".



            Throwing away point 2 completely destroys TypeScript's type system. Not an option.



            Throwing away point 3 also completely destroys TypeScript's type system.



            Throwing away point 4 is fine and makes you, the programmer, think about whether or not the object you're dealing with is possibly an alias for a subtype of the thing you think you have.



            The "missing feature" to make this legal but not contradictory is Exact Types, which would allow you to declare a new kind of type that wasn't subject to point #2. If this feature existed, it would presumably be possible to make Object.keys return keyof T only for Ts which were declared as exact.







            share|improve this answer














            share|improve this answer



            share|improve this answer








            edited 21 mins ago









            Tholle

            38.1k54264




            38.1k54264










            answered 1 hour ago









            Ryan CavanaughRyan Cavanaugh

            100k27171180




            100k27171180













            • However, there is common case when point 3. is excluded, when for example T is inferred and is guaranteed to be precise: const f: <T>(t: T) => void = (t) => { Object.keys(t).forEach(k => t[k]) }. I have lots of places like that in my code, where I really want Object.keys() to return (keyof T)[].

              – artem
              1 hour ago





















            • However, there is common case when point 3. is excluded, when for example T is inferred and is guaranteed to be precise: const f: <T>(t: T) => void = (t) => { Object.keys(t).forEach(k => t[k]) }. I have lots of places like that in my code, where I really want Object.keys() to return (keyof T)[].

              – artem
              1 hour ago



















            However, there is common case when point 3. is excluded, when for example T is inferred and is guaranteed to be precise: const f: <T>(t: T) => void = (t) => { Object.keys(t).forEach(k => t[k]) }. I have lots of places like that in my code, where I really want Object.keys() to return (keyof T)[].

            – artem
            1 hour ago







            However, there is common case when point 3. is excluded, when for example T is inferred and is guaranteed to be precise: const f: <T>(t: T) => void = (t) => { Object.keys(t).forEach(k => t[k]) }. I have lots of places like that in my code, where I really want Object.keys() to return (keyof T)[].

            – artem
            1 hour ago















            0














            I opened and closed a PR today related to this topic.
            My PR was only focusing on the case where keys are coming from an enum of strings.



            In this precise case, it does not seem that inheritance is feasible.



            I need to double check before reopening it https://github.com/Microsoft/TypeScript/pull/30228






            share|improve this answer




























              0














              I opened and closed a PR today related to this topic.
              My PR was only focusing on the case where keys are coming from an enum of strings.



              In this precise case, it does not seem that inheritance is feasible.



              I need to double check before reopening it https://github.com/Microsoft/TypeScript/pull/30228






              share|improve this answer


























                0












                0








                0







                I opened and closed a PR today related to this topic.
                My PR was only focusing on the case where keys are coming from an enum of strings.



                In this precise case, it does not seem that inheritance is feasible.



                I need to double check before reopening it https://github.com/Microsoft/TypeScript/pull/30228






                share|improve this answer













                I opened and closed a PR today related to this topic.
                My PR was only focusing on the case where keys are coming from an enum of strings.



                In this precise case, it does not seem that inheritance is feasible.



                I need to double check before reopening it https://github.com/Microsoft/TypeScript/pull/30228







                share|improve this answer












                share|improve this answer



                share|improve this answer










                answered 25 mins ago









                DubZzzDubZzz

                306




                306






























                    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%2f55012174%2fwhy-doesnt-object-keys-return-a-keyof-type-in-typescript%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

                    IEEEtran - How to include ORCID in TeX/PDF with PdfLatexIs there a standard way to include ORCID in TeX /...

                    Cicindela nigrior Przypisy | Menu nawigacyjneCicindela varians unicolorManual for the Identification of the...

                    Glossaries-extra: Adding glossaries package to “Clas­sicTh­e­sis” template by Dr. André Miede v. 4.6 ...