Typescript Advanced Type NonFunctionPropertyNames Explained












1















I was trying to get deeper understanding of advanced types in Typescript one of those types as example is NonFunctionPropertyNames which extracts only a properties of a given object.



type NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];


I can understand the first part inside the curly brackets '{ [K in keyof T]: T[K] extends Function ? never : K }', we are declaring an object and exclude the properties which extend Function. What puzzles me is the part after the curly brackets [keyof T]. This looks like a definition of an array {...}[keyof T] but it in fact returns object.
Can someone explain why only the curly brackets part is not enough for declaring the type, and what is the role of [keyof T].










share|improve this question


















  • 1





    Maybe this explanation helps: youtu.be/9Swrzqr4MSs?t=1319 :)

    – Titian Cernicova-Dragomir
    yesterday











  • @TitianCernicova-Dragomir Thank you, this made the things clear. So the key was that this constructions (let x:T[keyof T]) is actually returning all the types of the properties and methods inside T, this is not immediately clear. I was under the impression that getting the type of something will require the usage of typeof. Can you please give a good example where typeof in Typescript will be used ?

    – Hivaga
    yesterday













  • typeof is used whenever you want to get the type of a value. You can use typeof variable to get the type of the variable, or typeof Class to get the type of the class (the class name represents the type of the instance, not the type of the class (ie the static part of the class))

    – Titian Cernicova-Dragomir
    yesterday
















1















I was trying to get deeper understanding of advanced types in Typescript one of those types as example is NonFunctionPropertyNames which extracts only a properties of a given object.



type NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];


I can understand the first part inside the curly brackets '{ [K in keyof T]: T[K] extends Function ? never : K }', we are declaring an object and exclude the properties which extend Function. What puzzles me is the part after the curly brackets [keyof T]. This looks like a definition of an array {...}[keyof T] but it in fact returns object.
Can someone explain why only the curly brackets part is not enough for declaring the type, and what is the role of [keyof T].










share|improve this question


















  • 1





    Maybe this explanation helps: youtu.be/9Swrzqr4MSs?t=1319 :)

    – Titian Cernicova-Dragomir
    yesterday











  • @TitianCernicova-Dragomir Thank you, this made the things clear. So the key was that this constructions (let x:T[keyof T]) is actually returning all the types of the properties and methods inside T, this is not immediately clear. I was under the impression that getting the type of something will require the usage of typeof. Can you please give a good example where typeof in Typescript will be used ?

    – Hivaga
    yesterday













  • typeof is used whenever you want to get the type of a value. You can use typeof variable to get the type of the variable, or typeof Class to get the type of the class (the class name represents the type of the instance, not the type of the class (ie the static part of the class))

    – Titian Cernicova-Dragomir
    yesterday














1












1








1








I was trying to get deeper understanding of advanced types in Typescript one of those types as example is NonFunctionPropertyNames which extracts only a properties of a given object.



type NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];


I can understand the first part inside the curly brackets '{ [K in keyof T]: T[K] extends Function ? never : K }', we are declaring an object and exclude the properties which extend Function. What puzzles me is the part after the curly brackets [keyof T]. This looks like a definition of an array {...}[keyof T] but it in fact returns object.
Can someone explain why only the curly brackets part is not enough for declaring the type, and what is the role of [keyof T].










share|improve this question














I was trying to get deeper understanding of advanced types in Typescript one of those types as example is NonFunctionPropertyNames which extracts only a properties of a given object.



type NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T];


I can understand the first part inside the curly brackets '{ [K in keyof T]: T[K] extends Function ? never : K }', we are declaring an object and exclude the properties which extend Function. What puzzles me is the part after the curly brackets [keyof T]. This looks like a definition of an array {...}[keyof T] but it in fact returns object.
Can someone explain why only the curly brackets part is not enough for declaring the type, and what is the role of [keyof T].







typescript typescript-typings






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked yesterday









HivagaHivaga

319213




319213








  • 1





    Maybe this explanation helps: youtu.be/9Swrzqr4MSs?t=1319 :)

    – Titian Cernicova-Dragomir
    yesterday











  • @TitianCernicova-Dragomir Thank you, this made the things clear. So the key was that this constructions (let x:T[keyof T]) is actually returning all the types of the properties and methods inside T, this is not immediately clear. I was under the impression that getting the type of something will require the usage of typeof. Can you please give a good example where typeof in Typescript will be used ?

    – Hivaga
    yesterday













  • typeof is used whenever you want to get the type of a value. You can use typeof variable to get the type of the variable, or typeof Class to get the type of the class (the class name represents the type of the instance, not the type of the class (ie the static part of the class))

    – Titian Cernicova-Dragomir
    yesterday














  • 1





    Maybe this explanation helps: youtu.be/9Swrzqr4MSs?t=1319 :)

    – Titian Cernicova-Dragomir
    yesterday











  • @TitianCernicova-Dragomir Thank you, this made the things clear. So the key was that this constructions (let x:T[keyof T]) is actually returning all the types of the properties and methods inside T, this is not immediately clear. I was under the impression that getting the type of something will require the usage of typeof. Can you please give a good example where typeof in Typescript will be used ?

    – Hivaga
    yesterday













  • typeof is used whenever you want to get the type of a value. You can use typeof variable to get the type of the variable, or typeof Class to get the type of the class (the class name represents the type of the instance, not the type of the class (ie the static part of the class))

    – Titian Cernicova-Dragomir
    yesterday








1




1





Maybe this explanation helps: youtu.be/9Swrzqr4MSs?t=1319 :)

– Titian Cernicova-Dragomir
yesterday





Maybe this explanation helps: youtu.be/9Swrzqr4MSs?t=1319 :)

– Titian Cernicova-Dragomir
yesterday













@TitianCernicova-Dragomir Thank you, this made the things clear. So the key was that this constructions (let x:T[keyof T]) is actually returning all the types of the properties and methods inside T, this is not immediately clear. I was under the impression that getting the type of something will require the usage of typeof. Can you please give a good example where typeof in Typescript will be used ?

– Hivaga
yesterday







@TitianCernicova-Dragomir Thank you, this made the things clear. So the key was that this constructions (let x:T[keyof T]) is actually returning all the types of the properties and methods inside T, this is not immediately clear. I was under the impression that getting the type of something will require the usage of typeof. Can you please give a good example where typeof in Typescript will be used ?

– Hivaga
yesterday















typeof is used whenever you want to get the type of a value. You can use typeof variable to get the type of the variable, or typeof Class to get the type of the class (the class name represents the type of the instance, not the type of the class (ie the static part of the class))

– Titian Cernicova-Dragomir
yesterday





typeof is used whenever you want to get the type of a value. You can use typeof variable to get the type of the variable, or typeof Class to get the type of the class (the class name represents the type of the instance, not the type of the class (ie the static part of the class))

– Titian Cernicova-Dragomir
yesterday












1 Answer
1






active

oldest

votes


















1














What you are seeing there is a type query. If you index into an object type you get the type of that property. For example:



type Foo = { foo: number }['foo'] // is number


If you are indexing using a union of several properties you get a union of all the property types:



type FooBar = { foo: number, bar: string, baz: boolean }['foo' | 'bar']  // string | number


If you index using all keys you get the a union of all property types:



type FooBarBaz = { foo: number, bar: string, baz: boolean }['foo' | 'bar' | 'baz']  // string | number | boolean


But to get a union of all property names you can use keyof so the above type can also be written as:



type O =  { foo: number, bar: string, baz: boolean }
type FooBarBaz = O[keyof O] // string | number | boolean


The type { [K in keyof T]: K } evaluates to an object type, where the keys are typed as the same literal type representing the key:



type O =  { foo: number, bar: string, baz: boolean }
type FooBarBaz = { [K in keyof O]: K } // { foo: "foo"; bar: "bar"; baz: "baz"; }


What the conditional type does is make some of those keys, not the same type as the literal type represeting the key, but types them as never instead:



type O =  { foo: number, bar: string, baz: () => boolean } // baz is a function now
type NonFunctionPropertyNames = { [K in keyof O]: O[K] extends Function ? never: K } // { foo: "foo"; bar: "bar"; baz: never; }


So the new type still has all the keys of the original, but some are typed as the literal type of the corresponding key, and some are typed as never. What we want is a union with all the value types of the keys in the type we just constructed, and we can use keyof O as before (since the type has the same keys as O):



type O =  { foo: number, bar: string, baz: () => boolean } // baz is a function now
type NonFunctionPropertyNames = { [K in keyof O]: O[K] extends Function ? never: K }[keyof O] // "foo" | "bar" | never = "foo" | "bar" ;


never is always removed from unions, so we get at the end a union of just those object keys that were not never.



Make O a type parameter and you have a reusable type to get the non function keys:



type NonFunctionPropertyNames<O> = { [K in keyof O]: O[K] extends Function ? never : K }[keyof O]
type Foo = { foo: number, bar: string, baz: () => boolean } // baz is a function now
type NonFUnctionKeysOfFoo = NonFunctionPropertyNames<Foo> // "foo" | "bar"





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%2f54251847%2ftypescript-advanced-type-nonfunctionpropertynames-explained%23new-answer', 'question_page');
    }
    );

    Post as a guest















    Required, but never shown

























    1 Answer
    1






    active

    oldest

    votes








    1 Answer
    1






    active

    oldest

    votes









    active

    oldest

    votes






    active

    oldest

    votes









    1














    What you are seeing there is a type query. If you index into an object type you get the type of that property. For example:



    type Foo = { foo: number }['foo'] // is number


    If you are indexing using a union of several properties you get a union of all the property types:



    type FooBar = { foo: number, bar: string, baz: boolean }['foo' | 'bar']  // string | number


    If you index using all keys you get the a union of all property types:



    type FooBarBaz = { foo: number, bar: string, baz: boolean }['foo' | 'bar' | 'baz']  // string | number | boolean


    But to get a union of all property names you can use keyof so the above type can also be written as:



    type O =  { foo: number, bar: string, baz: boolean }
    type FooBarBaz = O[keyof O] // string | number | boolean


    The type { [K in keyof T]: K } evaluates to an object type, where the keys are typed as the same literal type representing the key:



    type O =  { foo: number, bar: string, baz: boolean }
    type FooBarBaz = { [K in keyof O]: K } // { foo: "foo"; bar: "bar"; baz: "baz"; }


    What the conditional type does is make some of those keys, not the same type as the literal type represeting the key, but types them as never instead:



    type O =  { foo: number, bar: string, baz: () => boolean } // baz is a function now
    type NonFunctionPropertyNames = { [K in keyof O]: O[K] extends Function ? never: K } // { foo: "foo"; bar: "bar"; baz: never; }


    So the new type still has all the keys of the original, but some are typed as the literal type of the corresponding key, and some are typed as never. What we want is a union with all the value types of the keys in the type we just constructed, and we can use keyof O as before (since the type has the same keys as O):



    type O =  { foo: number, bar: string, baz: () => boolean } // baz is a function now
    type NonFunctionPropertyNames = { [K in keyof O]: O[K] extends Function ? never: K }[keyof O] // "foo" | "bar" | never = "foo" | "bar" ;


    never is always removed from unions, so we get at the end a union of just those object keys that were not never.



    Make O a type parameter and you have a reusable type to get the non function keys:



    type NonFunctionPropertyNames<O> = { [K in keyof O]: O[K] extends Function ? never : K }[keyof O]
    type Foo = { foo: number, bar: string, baz: () => boolean } // baz is a function now
    type NonFUnctionKeysOfFoo = NonFunctionPropertyNames<Foo> // "foo" | "bar"





    share|improve this answer




























      1














      What you are seeing there is a type query. If you index into an object type you get the type of that property. For example:



      type Foo = { foo: number }['foo'] // is number


      If you are indexing using a union of several properties you get a union of all the property types:



      type FooBar = { foo: number, bar: string, baz: boolean }['foo' | 'bar']  // string | number


      If you index using all keys you get the a union of all property types:



      type FooBarBaz = { foo: number, bar: string, baz: boolean }['foo' | 'bar' | 'baz']  // string | number | boolean


      But to get a union of all property names you can use keyof so the above type can also be written as:



      type O =  { foo: number, bar: string, baz: boolean }
      type FooBarBaz = O[keyof O] // string | number | boolean


      The type { [K in keyof T]: K } evaluates to an object type, where the keys are typed as the same literal type representing the key:



      type O =  { foo: number, bar: string, baz: boolean }
      type FooBarBaz = { [K in keyof O]: K } // { foo: "foo"; bar: "bar"; baz: "baz"; }


      What the conditional type does is make some of those keys, not the same type as the literal type represeting the key, but types them as never instead:



      type O =  { foo: number, bar: string, baz: () => boolean } // baz is a function now
      type NonFunctionPropertyNames = { [K in keyof O]: O[K] extends Function ? never: K } // { foo: "foo"; bar: "bar"; baz: never; }


      So the new type still has all the keys of the original, but some are typed as the literal type of the corresponding key, and some are typed as never. What we want is a union with all the value types of the keys in the type we just constructed, and we can use keyof O as before (since the type has the same keys as O):



      type O =  { foo: number, bar: string, baz: () => boolean } // baz is a function now
      type NonFunctionPropertyNames = { [K in keyof O]: O[K] extends Function ? never: K }[keyof O] // "foo" | "bar" | never = "foo" | "bar" ;


      never is always removed from unions, so we get at the end a union of just those object keys that were not never.



      Make O a type parameter and you have a reusable type to get the non function keys:



      type NonFunctionPropertyNames<O> = { [K in keyof O]: O[K] extends Function ? never : K }[keyof O]
      type Foo = { foo: number, bar: string, baz: () => boolean } // baz is a function now
      type NonFUnctionKeysOfFoo = NonFunctionPropertyNames<Foo> // "foo" | "bar"





      share|improve this answer


























        1












        1








        1







        What you are seeing there is a type query. If you index into an object type you get the type of that property. For example:



        type Foo = { foo: number }['foo'] // is number


        If you are indexing using a union of several properties you get a union of all the property types:



        type FooBar = { foo: number, bar: string, baz: boolean }['foo' | 'bar']  // string | number


        If you index using all keys you get the a union of all property types:



        type FooBarBaz = { foo: number, bar: string, baz: boolean }['foo' | 'bar' | 'baz']  // string | number | boolean


        But to get a union of all property names you can use keyof so the above type can also be written as:



        type O =  { foo: number, bar: string, baz: boolean }
        type FooBarBaz = O[keyof O] // string | number | boolean


        The type { [K in keyof T]: K } evaluates to an object type, where the keys are typed as the same literal type representing the key:



        type O =  { foo: number, bar: string, baz: boolean }
        type FooBarBaz = { [K in keyof O]: K } // { foo: "foo"; bar: "bar"; baz: "baz"; }


        What the conditional type does is make some of those keys, not the same type as the literal type represeting the key, but types them as never instead:



        type O =  { foo: number, bar: string, baz: () => boolean } // baz is a function now
        type NonFunctionPropertyNames = { [K in keyof O]: O[K] extends Function ? never: K } // { foo: "foo"; bar: "bar"; baz: never; }


        So the new type still has all the keys of the original, but some are typed as the literal type of the corresponding key, and some are typed as never. What we want is a union with all the value types of the keys in the type we just constructed, and we can use keyof O as before (since the type has the same keys as O):



        type O =  { foo: number, bar: string, baz: () => boolean } // baz is a function now
        type NonFunctionPropertyNames = { [K in keyof O]: O[K] extends Function ? never: K }[keyof O] // "foo" | "bar" | never = "foo" | "bar" ;


        never is always removed from unions, so we get at the end a union of just those object keys that were not never.



        Make O a type parameter and you have a reusable type to get the non function keys:



        type NonFunctionPropertyNames<O> = { [K in keyof O]: O[K] extends Function ? never : K }[keyof O]
        type Foo = { foo: number, bar: string, baz: () => boolean } // baz is a function now
        type NonFUnctionKeysOfFoo = NonFunctionPropertyNames<Foo> // "foo" | "bar"





        share|improve this answer













        What you are seeing there is a type query. If you index into an object type you get the type of that property. For example:



        type Foo = { foo: number }['foo'] // is number


        If you are indexing using a union of several properties you get a union of all the property types:



        type FooBar = { foo: number, bar: string, baz: boolean }['foo' | 'bar']  // string | number


        If you index using all keys you get the a union of all property types:



        type FooBarBaz = { foo: number, bar: string, baz: boolean }['foo' | 'bar' | 'baz']  // string | number | boolean


        But to get a union of all property names you can use keyof so the above type can also be written as:



        type O =  { foo: number, bar: string, baz: boolean }
        type FooBarBaz = O[keyof O] // string | number | boolean


        The type { [K in keyof T]: K } evaluates to an object type, where the keys are typed as the same literal type representing the key:



        type O =  { foo: number, bar: string, baz: boolean }
        type FooBarBaz = { [K in keyof O]: K } // { foo: "foo"; bar: "bar"; baz: "baz"; }


        What the conditional type does is make some of those keys, not the same type as the literal type represeting the key, but types them as never instead:



        type O =  { foo: number, bar: string, baz: () => boolean } // baz is a function now
        type NonFunctionPropertyNames = { [K in keyof O]: O[K] extends Function ? never: K } // { foo: "foo"; bar: "bar"; baz: never; }


        So the new type still has all the keys of the original, but some are typed as the literal type of the corresponding key, and some are typed as never. What we want is a union with all the value types of the keys in the type we just constructed, and we can use keyof O as before (since the type has the same keys as O):



        type O =  { foo: number, bar: string, baz: () => boolean } // baz is a function now
        type NonFunctionPropertyNames = { [K in keyof O]: O[K] extends Function ? never: K }[keyof O] // "foo" | "bar" | never = "foo" | "bar" ;


        never is always removed from unions, so we get at the end a union of just those object keys that were not never.



        Make O a type parameter and you have a reusable type to get the non function keys:



        type NonFunctionPropertyNames<O> = { [K in keyof O]: O[K] extends Function ? never : K }[keyof O]
        type Foo = { foo: number, bar: string, baz: () => boolean } // baz is a function now
        type NonFUnctionKeysOfFoo = NonFunctionPropertyNames<Foo> // "foo" | "bar"






        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered yesterday









        Titian Cernicova-DragomirTitian Cernicova-Dragomir

        59.9k33553




        59.9k33553






























            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%2f54251847%2ftypescript-advanced-type-nonfunctionpropertynames-explained%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

            Liquibase includeAll doesn't find base path

            How to use setInterval in EJS file?

            Petrus Granier-Deferre