TypeScript extra keys in nested objectsWhat is TypeScript and why would I use it in place of JavaScript?How do you explicitly set a new property on `window` in TypeScript?Type definition in object literal in TypeScriptget and set in TypeScriptHow can I create an object based on an interface file definition in TypeScript?TypeScript Objects as Dictionary types as in C#Are strongly-typed functions as parameters possible in TypeScript?TypeScript Converting a String to a numberHow do I cast a JSON object to a typescript classTypescript: Interfaces vs Types

Impact of throwing away fruit waste on a peak > 3200 m above a glacier

How do I run a game when my PCs have different approaches to combat?

How can I deal with someone that wants to kill something that isn't supposed to be killed?

Is the apartment I want to rent a scam?

Sometimes you are this word with three vowels

What's the 1 inch size square knob sticking out of wall?

what to say when a company asks you why someone (a friend) who was fired left?

Why are MEMS in QFN packages?

Can I pay with HKD in Macau or Shenzhen?

Is it OK to accept a job opportunity while planning on not taking it?

Raw curve25519 public key points

Are glider winch launches rarer in the USA than in the rest of the world? Why?

The seven story archetypes. Are they truly all of them?

Using "Kollege" as "university friend"?

Historicity doubted by Romans

What exactly makes a General Products hull nearly indestructible?

How to repair basic cable/wire issue for household appliances

What the purpose of the fuel shutoff valve?

Are gangsters hired to attack people at a train station classified as a terrorist attack?

Sextortion with actual password not found in leaks

Company requiring me to let them review research from before I was hired

What is an Eternal Word™?

Should i describe deeply a character before killing it?

What does the Find Familiar spell target?



TypeScript extra keys in nested objects


What is TypeScript and why would I use it in place of JavaScript?How do you explicitly set a new property on `window` in TypeScript?Type definition in object literal in TypeScriptget and set in TypeScriptHow can I create an object based on an interface file definition in TypeScript?TypeScript Objects as Dictionary types as in C#Are strongly-typed functions as parameters possible in TypeScript?TypeScript Converting a String to a numberHow do I cast a JSON object to a typescript classTypescript: Interfaces vs Types






.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty margin-bottom:0;








2















My problem can be summed up with this little snippet (here's a larger, interactive example in the Playground):



type X = x: number;
type Y = y: number;
type XXY = x: X & Y;
let xxy: XXY =
x:
x: 1,
notValid: 1 // <--- this is not an error :(
,
y: 1
;


Given that X and Y are derived in another way (and so I can't just write the XXY type by hand), how can I make it so that unknown keys in the nested object are treated as invalid?










share|improve this question

















  • 1





    github.com/Microsoft/TypeScript/issues/18075

    – ritaj
    Mar 26 at 14:59











  • Interesting scenario. Also, it's interesting that if you remove y: 1, instead of complaining about y missing, it complains about notValid being invalid. prntscr.com/n36p0v

    – briosheje
    Mar 26 at 15:00

















2















My problem can be summed up with this little snippet (here's a larger, interactive example in the Playground):



type X = x: number;
type Y = y: number;
type XXY = x: X & Y;
let xxy: XXY =
x:
x: 1,
notValid: 1 // <--- this is not an error :(
,
y: 1
;


Given that X and Y are derived in another way (and so I can't just write the XXY type by hand), how can I make it so that unknown keys in the nested object are treated as invalid?










share|improve this question

















  • 1





    github.com/Microsoft/TypeScript/issues/18075

    – ritaj
    Mar 26 at 14:59











  • Interesting scenario. Also, it's interesting that if you remove y: 1, instead of complaining about y missing, it complains about notValid being invalid. prntscr.com/n36p0v

    – briosheje
    Mar 26 at 15:00













2












2








2








My problem can be summed up with this little snippet (here's a larger, interactive example in the Playground):



type X = x: number;
type Y = y: number;
type XXY = x: X & Y;
let xxy: XXY =
x:
x: 1,
notValid: 1 // <--- this is not an error :(
,
y: 1
;


Given that X and Y are derived in another way (and so I can't just write the XXY type by hand), how can I make it so that unknown keys in the nested object are treated as invalid?










share|improve this question














My problem can be summed up with this little snippet (here's a larger, interactive example in the Playground):



type X = x: number;
type Y = y: number;
type XXY = x: X & Y;
let xxy: XXY =
x:
x: 1,
notValid: 1 // <--- this is not an error :(
,
y: 1
;


Given that X and Y are derived in another way (and so I can't just write the XXY type by hand), how can I make it so that unknown keys in the nested object are treated as invalid?







typescript






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked Mar 26 at 14:51









nickfnickf

386k175 gold badges592 silver badges692 bronze badges




386k175 gold badges592 silver badges692 bronze badges







  • 1





    github.com/Microsoft/TypeScript/issues/18075

    – ritaj
    Mar 26 at 14:59











  • Interesting scenario. Also, it's interesting that if you remove y: 1, instead of complaining about y missing, it complains about notValid being invalid. prntscr.com/n36p0v

    – briosheje
    Mar 26 at 15:00












  • 1





    github.com/Microsoft/TypeScript/issues/18075

    – ritaj
    Mar 26 at 14:59











  • Interesting scenario. Also, it's interesting that if you remove y: 1, instead of complaining about y missing, it complains about notValid being invalid. prntscr.com/n36p0v

    – briosheje
    Mar 26 at 15:00







1




1





github.com/Microsoft/TypeScript/issues/18075

– ritaj
Mar 26 at 14:59





github.com/Microsoft/TypeScript/issues/18075

– ritaj
Mar 26 at 14:59













Interesting scenario. Also, it's interesting that if you remove y: 1, instead of complaining about y missing, it complains about notValid being invalid. prntscr.com/n36p0v

– briosheje
Mar 26 at 15:00





Interesting scenario. Also, it's interesting that if you remove y: 1, instead of complaining about y missing, it complains about notValid being invalid. prntscr.com/n36p0v

– briosheje
Mar 26 at 15:00












1 Answer
1






active

oldest

votes


















1














This is a known bug where excess property checking doesn't apply to nested types involving unions and intersections in the way that people expect. Excess property checking is kind of an add-on to the type system that only applies to object literals, so when it doesn't apply, things fall back to the structural subtyping rule where type a: A, b: B is a subtype of a: A, and so a value of the former type should be assignable to a variable of the latter type. You might want to head over to the issue in Github and give it a 👍 or explain your use case if you think it's more compelling than the ones already listed there. Hopefully there will be a fix someday.



Until then, there are workarounds. The type-level equivalent to excess property checks are so-called exact types, which don't exist in TypeScript as concrete types. There are ways to simulate them using generic helper functions and type inference... in your case it would look something like this:



type Exactly<T, U extends T> = T extends object ?
[K in keyof U]: K extends keyof T ? Exactly<T[K], U[K]> : never
: T

const asXXY = <T extends XXY>(x: T & Exactly<XXY, T>): T => x;

let xxy = asXXY(
x:
x: 1,
notValid: 1 // error!
,
y: 1
);


That's the error you want, right?



How it works: The helper function asXXY<T extends XXY>(t: T & Exactly<XXY, T>) infers the generic type T to be the type of the passed-in parameter t. Then it tries to evaluate the intersection T & Exactly<XXY, T>. If t is assignable to T & Exactly<XXY, T>, the check succeeds and the function is callable. Otherwise, there will be an error somewhere on t showing where they differ.



And Exactly<T, U extends T> basically walks down recursively through U, keeping it the same as long as it matches T... otherwise it sets the property to never.



Let's expand on this difference for the above case: The inferred type for T was



 x: x: number; notValid: number; ; y: number; 


Is that assignable to T & Exactly<XXY, T>? Well, what's Exactly<XXY, T>? It's turns out to be



 x: x: number; notValid: never; ; y: number; 


which is what happens when you drill down into type T and notice that notValid can't be found inside XXY.



The intersection T & Exactly<XXY, T> is essentially just Exactly<XXY, T>, so now the compiler is comparing the passed-in parameter to the type



 x: x: number; notValid: never; ; y: number; 


which it isn't. The notValid type is a number, not a never... so the compiler complains in exactly the place you want.



Okay, hope that helps. Good luck!






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%2f55360087%2ftypescript-extra-keys-in-nested-objects%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














    This is a known bug where excess property checking doesn't apply to nested types involving unions and intersections in the way that people expect. Excess property checking is kind of an add-on to the type system that only applies to object literals, so when it doesn't apply, things fall back to the structural subtyping rule where type a: A, b: B is a subtype of a: A, and so a value of the former type should be assignable to a variable of the latter type. You might want to head over to the issue in Github and give it a 👍 or explain your use case if you think it's more compelling than the ones already listed there. Hopefully there will be a fix someday.



    Until then, there are workarounds. The type-level equivalent to excess property checks are so-called exact types, which don't exist in TypeScript as concrete types. There are ways to simulate them using generic helper functions and type inference... in your case it would look something like this:



    type Exactly<T, U extends T> = T extends object ?
    [K in keyof U]: K extends keyof T ? Exactly<T[K], U[K]> : never
    : T

    const asXXY = <T extends XXY>(x: T & Exactly<XXY, T>): T => x;

    let xxy = asXXY(
    x:
    x: 1,
    notValid: 1 // error!
    ,
    y: 1
    );


    That's the error you want, right?



    How it works: The helper function asXXY<T extends XXY>(t: T & Exactly<XXY, T>) infers the generic type T to be the type of the passed-in parameter t. Then it tries to evaluate the intersection T & Exactly<XXY, T>. If t is assignable to T & Exactly<XXY, T>, the check succeeds and the function is callable. Otherwise, there will be an error somewhere on t showing where they differ.



    And Exactly<T, U extends T> basically walks down recursively through U, keeping it the same as long as it matches T... otherwise it sets the property to never.



    Let's expand on this difference for the above case: The inferred type for T was



     x: x: number; notValid: number; ; y: number; 


    Is that assignable to T & Exactly<XXY, T>? Well, what's Exactly<XXY, T>? It's turns out to be



     x: x: number; notValid: never; ; y: number; 


    which is what happens when you drill down into type T and notice that notValid can't be found inside XXY.



    The intersection T & Exactly<XXY, T> is essentially just Exactly<XXY, T>, so now the compiler is comparing the passed-in parameter to the type



     x: x: number; notValid: never; ; y: number; 


    which it isn't. The notValid type is a number, not a never... so the compiler complains in exactly the place you want.



    Okay, hope that helps. Good luck!






    share|improve this answer



























      1














      This is a known bug where excess property checking doesn't apply to nested types involving unions and intersections in the way that people expect. Excess property checking is kind of an add-on to the type system that only applies to object literals, so when it doesn't apply, things fall back to the structural subtyping rule where type a: A, b: B is a subtype of a: A, and so a value of the former type should be assignable to a variable of the latter type. You might want to head over to the issue in Github and give it a 👍 or explain your use case if you think it's more compelling than the ones already listed there. Hopefully there will be a fix someday.



      Until then, there are workarounds. The type-level equivalent to excess property checks are so-called exact types, which don't exist in TypeScript as concrete types. There are ways to simulate them using generic helper functions and type inference... in your case it would look something like this:



      type Exactly<T, U extends T> = T extends object ?
      [K in keyof U]: K extends keyof T ? Exactly<T[K], U[K]> : never
      : T

      const asXXY = <T extends XXY>(x: T & Exactly<XXY, T>): T => x;

      let xxy = asXXY(
      x:
      x: 1,
      notValid: 1 // error!
      ,
      y: 1
      );


      That's the error you want, right?



      How it works: The helper function asXXY<T extends XXY>(t: T & Exactly<XXY, T>) infers the generic type T to be the type of the passed-in parameter t. Then it tries to evaluate the intersection T & Exactly<XXY, T>. If t is assignable to T & Exactly<XXY, T>, the check succeeds and the function is callable. Otherwise, there will be an error somewhere on t showing where they differ.



      And Exactly<T, U extends T> basically walks down recursively through U, keeping it the same as long as it matches T... otherwise it sets the property to never.



      Let's expand on this difference for the above case: The inferred type for T was



       x: x: number; notValid: number; ; y: number; 


      Is that assignable to T & Exactly<XXY, T>? Well, what's Exactly<XXY, T>? It's turns out to be



       x: x: number; notValid: never; ; y: number; 


      which is what happens when you drill down into type T and notice that notValid can't be found inside XXY.



      The intersection T & Exactly<XXY, T> is essentially just Exactly<XXY, T>, so now the compiler is comparing the passed-in parameter to the type



       x: x: number; notValid: never; ; y: number; 


      which it isn't. The notValid type is a number, not a never... so the compiler complains in exactly the place you want.



      Okay, hope that helps. Good luck!






      share|improve this answer

























        1












        1








        1







        This is a known bug where excess property checking doesn't apply to nested types involving unions and intersections in the way that people expect. Excess property checking is kind of an add-on to the type system that only applies to object literals, so when it doesn't apply, things fall back to the structural subtyping rule where type a: A, b: B is a subtype of a: A, and so a value of the former type should be assignable to a variable of the latter type. You might want to head over to the issue in Github and give it a 👍 or explain your use case if you think it's more compelling than the ones already listed there. Hopefully there will be a fix someday.



        Until then, there are workarounds. The type-level equivalent to excess property checks are so-called exact types, which don't exist in TypeScript as concrete types. There are ways to simulate them using generic helper functions and type inference... in your case it would look something like this:



        type Exactly<T, U extends T> = T extends object ?
        [K in keyof U]: K extends keyof T ? Exactly<T[K], U[K]> : never
        : T

        const asXXY = <T extends XXY>(x: T & Exactly<XXY, T>): T => x;

        let xxy = asXXY(
        x:
        x: 1,
        notValid: 1 // error!
        ,
        y: 1
        );


        That's the error you want, right?



        How it works: The helper function asXXY<T extends XXY>(t: T & Exactly<XXY, T>) infers the generic type T to be the type of the passed-in parameter t. Then it tries to evaluate the intersection T & Exactly<XXY, T>. If t is assignable to T & Exactly<XXY, T>, the check succeeds and the function is callable. Otherwise, there will be an error somewhere on t showing where they differ.



        And Exactly<T, U extends T> basically walks down recursively through U, keeping it the same as long as it matches T... otherwise it sets the property to never.



        Let's expand on this difference for the above case: The inferred type for T was



         x: x: number; notValid: number; ; y: number; 


        Is that assignable to T & Exactly<XXY, T>? Well, what's Exactly<XXY, T>? It's turns out to be



         x: x: number; notValid: never; ; y: number; 


        which is what happens when you drill down into type T and notice that notValid can't be found inside XXY.



        The intersection T & Exactly<XXY, T> is essentially just Exactly<XXY, T>, so now the compiler is comparing the passed-in parameter to the type



         x: x: number; notValid: never; ; y: number; 


        which it isn't. The notValid type is a number, not a never... so the compiler complains in exactly the place you want.



        Okay, hope that helps. Good luck!






        share|improve this answer













        This is a known bug where excess property checking doesn't apply to nested types involving unions and intersections in the way that people expect. Excess property checking is kind of an add-on to the type system that only applies to object literals, so when it doesn't apply, things fall back to the structural subtyping rule where type a: A, b: B is a subtype of a: A, and so a value of the former type should be assignable to a variable of the latter type. You might want to head over to the issue in Github and give it a 👍 or explain your use case if you think it's more compelling than the ones already listed there. Hopefully there will be a fix someday.



        Until then, there are workarounds. The type-level equivalent to excess property checks are so-called exact types, which don't exist in TypeScript as concrete types. There are ways to simulate them using generic helper functions and type inference... in your case it would look something like this:



        type Exactly<T, U extends T> = T extends object ?
        [K in keyof U]: K extends keyof T ? Exactly<T[K], U[K]> : never
        : T

        const asXXY = <T extends XXY>(x: T & Exactly<XXY, T>): T => x;

        let xxy = asXXY(
        x:
        x: 1,
        notValid: 1 // error!
        ,
        y: 1
        );


        That's the error you want, right?



        How it works: The helper function asXXY<T extends XXY>(t: T & Exactly<XXY, T>) infers the generic type T to be the type of the passed-in parameter t. Then it tries to evaluate the intersection T & Exactly<XXY, T>. If t is assignable to T & Exactly<XXY, T>, the check succeeds and the function is callable. Otherwise, there will be an error somewhere on t showing where they differ.



        And Exactly<T, U extends T> basically walks down recursively through U, keeping it the same as long as it matches T... otherwise it sets the property to never.



        Let's expand on this difference for the above case: The inferred type for T was



         x: x: number; notValid: number; ; y: number; 


        Is that assignable to T & Exactly<XXY, T>? Well, what's Exactly<XXY, T>? It's turns out to be



         x: x: number; notValid: never; ; y: number; 


        which is what happens when you drill down into type T and notice that notValid can't be found inside XXY.



        The intersection T & Exactly<XXY, T> is essentially just Exactly<XXY, T>, so now the compiler is comparing the passed-in parameter to the type



         x: x: number; notValid: never; ; y: number; 


        which it isn't. The notValid type is a number, not a never... so the compiler complains in exactly the place you want.



        Okay, hope that helps. Good luck!







        share|improve this answer












        share|improve this answer



        share|improve this answer










        answered Mar 26 at 15:49









        jcalzjcalz

        42.1k2 gold badges38 silver badges57 bronze badges




        42.1k2 gold badges38 silver badges57 bronze badges
















            Got a question that you can’t ask on public Stack Overflow? Learn more about sharing private information with Stack Overflow for Teams.







            Got a question that you can’t ask on public Stack Overflow? Learn more about sharing private information with Stack Overflow for Teams.



















            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%2f55360087%2ftypescript-extra-keys-in-nested-objects%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

            Kamusi Yaliyomo Aina za kamusi | Muundo wa kamusi | Faida za kamusi | Dhima ya picha katika kamusi | Marejeo | Tazama pia | Viungo vya nje | UrambazajiKuhusu kamusiGo-SwahiliWiki-KamusiKamusi ya Kiswahili na Kiingerezakuihariri na kuongeza habari

            Swift 4 - func physicsWorld not invoked on collision? The Next CEO of Stack OverflowHow to call Objective-C code from Swift#ifdef replacement in the Swift language@selector() in Swift?#pragma mark in Swift?Swift for loop: for index, element in array?dispatch_after - GCD in Swift?Swift Beta performance: sorting arraysSplit a String into an array in Swift?The use of Swift 3 @objc inference in Swift 4 mode is deprecated?How to optimize UITableViewCell, because my UITableView lags

            Access current req object everywhere in Node.js ExpressWhy are global variables considered bad practice? (node.js)Using req & res across functionsHow do I get the path to the current script with Node.js?What is Node.js' Connect, Express and “middleware”?Node.js w/ express error handling in callbackHow to access the GET parameters after “?” in Express?Modify Node.js req object parametersAccess “app” variable inside of ExpressJS/ConnectJS middleware?Node.js Express app - request objectAngular Http Module considered middleware?Session variables in ExpressJSAdd properties to the req object in expressjs with Typescript