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;
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
add a comment |
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
1
github.com/Microsoft/TypeScript/issues/18075
– ritaj
Mar 26 at 14:59
Interesting scenario. Also, it's interesting that if you removey: 1
, instead of complaining abouty
missing, it complains aboutnotValid
being invalid. prntscr.com/n36p0v
– briosheje
Mar 26 at 15:00
add a comment |
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
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
typescript
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 removey: 1
, instead of complaining abouty
missing, it complains aboutnotValid
being invalid. prntscr.com/n36p0v
– briosheje
Mar 26 at 15:00
add a comment |
1
github.com/Microsoft/TypeScript/issues/18075
– ritaj
Mar 26 at 14:59
Interesting scenario. Also, it's interesting that if you removey: 1
, instead of complaining abouty
missing, it complains aboutnotValid
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
add a comment |
1 Answer
1
active
oldest
votes
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!
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%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
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!
add a comment |
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!
add a comment |
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!
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!
answered Mar 26 at 15:49
jcalzjcalz
42.1k2 gold badges38 silver badges57 bronze badges
42.1k2 gold badges38 silver badges57 bronze badges
add a comment |
add a comment |
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.
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%2f55360087%2ftypescript-extra-keys-in-nested-objects%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
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 abouty
missing, it complains aboutnotValid
being invalid. prntscr.com/n36p0v– briosheje
Mar 26 at 15:00