'is not assignable to parameter of type' error with object literal












1















There is a problem with object literal type.



interface OptionalFoo {
foo?: number;
}

interface Bar {}

function foobarFn(foobar: OptionalFoo & Bar) {}

foobarFn({ bar: 1 }); // error

foobarFn({ bar: 1 } as { bar: number }); // ok

foobarFn({ bar: 1 } as { bar: 1 }); // ok!


Object literal with inferred type causes type error:




Argument of type '{ bar: number; }' is not assignable to parameter of type 'OptionalFoo & Bar'




But the problem is not the inference itself:



const bar = { bar: 1 }; // inferred { bar: number; }
foobarFn(bar); // ok!?


The same problem with spread syntax vs Object.assign:



foobarFn({...{ bar: 1 }}); // error

foobarFn(Object.assign({}, { bar: 1 })); // ok!?


Is there a way to achieve the behaviour with inferred object literal type (excess property check) without in-place object literal, e.g. with bar variable or function call like Object.assign({ bar: 1 })?










share|improve this question




















  • 1





    typescriptlang.org/docs/handbook/…

    – JB Nizet
    Nov 23 '18 at 10:42











  • @JBNizet Thanks for the reference, totally forgot about this term. Yes, that's it. I wonder if it's possible to trigger excess property check where it isn't applied by TS, like when an object is passed as a variable.

    – estus
    Nov 23 '18 at 11:02











  • @estus It's not just about that .. I think it's about weak types ...

    – Titian Cernicova-Dragomir
    Nov 23 '18 at 11:02













  • That would necessitate runtime checks, not compile-time checks.

    – JB Nizet
    Nov 23 '18 at 11:04
















1















There is a problem with object literal type.



interface OptionalFoo {
foo?: number;
}

interface Bar {}

function foobarFn(foobar: OptionalFoo & Bar) {}

foobarFn({ bar: 1 }); // error

foobarFn({ bar: 1 } as { bar: number }); // ok

foobarFn({ bar: 1 } as { bar: 1 }); // ok!


Object literal with inferred type causes type error:




Argument of type '{ bar: number; }' is not assignable to parameter of type 'OptionalFoo & Bar'




But the problem is not the inference itself:



const bar = { bar: 1 }; // inferred { bar: number; }
foobarFn(bar); // ok!?


The same problem with spread syntax vs Object.assign:



foobarFn({...{ bar: 1 }}); // error

foobarFn(Object.assign({}, { bar: 1 })); // ok!?


Is there a way to achieve the behaviour with inferred object literal type (excess property check) without in-place object literal, e.g. with bar variable or function call like Object.assign({ bar: 1 })?










share|improve this question




















  • 1





    typescriptlang.org/docs/handbook/…

    – JB Nizet
    Nov 23 '18 at 10:42











  • @JBNizet Thanks for the reference, totally forgot about this term. Yes, that's it. I wonder if it's possible to trigger excess property check where it isn't applied by TS, like when an object is passed as a variable.

    – estus
    Nov 23 '18 at 11:02











  • @estus It's not just about that .. I think it's about weak types ...

    – Titian Cernicova-Dragomir
    Nov 23 '18 at 11:02













  • That would necessitate runtime checks, not compile-time checks.

    – JB Nizet
    Nov 23 '18 at 11:04














1












1








1








There is a problem with object literal type.



interface OptionalFoo {
foo?: number;
}

interface Bar {}

function foobarFn(foobar: OptionalFoo & Bar) {}

foobarFn({ bar: 1 }); // error

foobarFn({ bar: 1 } as { bar: number }); // ok

foobarFn({ bar: 1 } as { bar: 1 }); // ok!


Object literal with inferred type causes type error:




Argument of type '{ bar: number; }' is not assignable to parameter of type 'OptionalFoo & Bar'




But the problem is not the inference itself:



const bar = { bar: 1 }; // inferred { bar: number; }
foobarFn(bar); // ok!?


The same problem with spread syntax vs Object.assign:



foobarFn({...{ bar: 1 }}); // error

foobarFn(Object.assign({}, { bar: 1 })); // ok!?


Is there a way to achieve the behaviour with inferred object literal type (excess property check) without in-place object literal, e.g. with bar variable or function call like Object.assign({ bar: 1 })?










share|improve this question
















There is a problem with object literal type.



interface OptionalFoo {
foo?: number;
}

interface Bar {}

function foobarFn(foobar: OptionalFoo & Bar) {}

foobarFn({ bar: 1 }); // error

foobarFn({ bar: 1 } as { bar: number }); // ok

foobarFn({ bar: 1 } as { bar: 1 }); // ok!


Object literal with inferred type causes type error:




Argument of type '{ bar: number; }' is not assignable to parameter of type 'OptionalFoo & Bar'




But the problem is not the inference itself:



const bar = { bar: 1 }; // inferred { bar: number; }
foobarFn(bar); // ok!?


The same problem with spread syntax vs Object.assign:



foobarFn({...{ bar: 1 }}); // error

foobarFn(Object.assign({}, { bar: 1 })); // ok!?


Is there a way to achieve the behaviour with inferred object literal type (excess property check) without in-place object literal, e.g. with bar variable or function call like Object.assign({ bar: 1 })?







typescript type-inference






share|improve this question















share|improve this question













share|improve this question




share|improve this question








edited Nov 23 '18 at 11:00







estus

















asked Nov 23 '18 at 10:30









estusestus

72.4k22106224




72.4k22106224








  • 1





    typescriptlang.org/docs/handbook/…

    – JB Nizet
    Nov 23 '18 at 10:42











  • @JBNizet Thanks for the reference, totally forgot about this term. Yes, that's it. I wonder if it's possible to trigger excess property check where it isn't applied by TS, like when an object is passed as a variable.

    – estus
    Nov 23 '18 at 11:02











  • @estus It's not just about that .. I think it's about weak types ...

    – Titian Cernicova-Dragomir
    Nov 23 '18 at 11:02













  • That would necessitate runtime checks, not compile-time checks.

    – JB Nizet
    Nov 23 '18 at 11:04














  • 1





    typescriptlang.org/docs/handbook/…

    – JB Nizet
    Nov 23 '18 at 10:42











  • @JBNizet Thanks for the reference, totally forgot about this term. Yes, that's it. I wonder if it's possible to trigger excess property check where it isn't applied by TS, like when an object is passed as a variable.

    – estus
    Nov 23 '18 at 11:02











  • @estus It's not just about that .. I think it's about weak types ...

    – Titian Cernicova-Dragomir
    Nov 23 '18 at 11:02













  • That would necessitate runtime checks, not compile-time checks.

    – JB Nizet
    Nov 23 '18 at 11:04








1




1





typescriptlang.org/docs/handbook/…

– JB Nizet
Nov 23 '18 at 10:42





typescriptlang.org/docs/handbook/…

– JB Nizet
Nov 23 '18 at 10:42













@JBNizet Thanks for the reference, totally forgot about this term. Yes, that's it. I wonder if it's possible to trigger excess property check where it isn't applied by TS, like when an object is passed as a variable.

– estus
Nov 23 '18 at 11:02





@JBNizet Thanks for the reference, totally forgot about this term. Yes, that's it. I wonder if it's possible to trigger excess property check where it isn't applied by TS, like when an object is passed as a variable.

– estus
Nov 23 '18 at 11:02













@estus It's not just about that .. I think it's about weak types ...

– Titian Cernicova-Dragomir
Nov 23 '18 at 11:02







@estus It's not just about that .. I think it's about weak types ...

– Titian Cernicova-Dragomir
Nov 23 '18 at 11:02















That would necessitate runtime checks, not compile-time checks.

– JB Nizet
Nov 23 '18 at 11:04





That would necessitate runtime checks, not compile-time checks.

– JB Nizet
Nov 23 '18 at 11:04












1 Answer
1






active

oldest

votes


















1














Just to make things clear this is not just about excess property checks. Excess property checks come into play when we assign an object literal directly to a location. In your case all the more surprising behavior occurs when assigning an object indirectly, which would normally be allowed under excess property checks.



function foo(o: { bar: number }) { }
foo({ bar: 0, foo: "" }) // error direct assignment
foo({ bar:0, foo: ""} as { bar:0, foo: "" }) // ok indirect


The surprising part, at least to me is thst another check (the weak type check) does not catch this error. Under weak type checks (as described here) if a type has only optional properties, and we try to assign a type that has no properties in common with it we should get an error:



function foo(o: { bar?: number }) { }
foo({ foo: "" }) // error under excess properties: Object literal may only specify known properties, and 'foo' does not exist in type
foo({ foo: ""} as { foo: "" }) // error under weak types: Type '{ foo: ""; }' has no properties in common with type '{ bar?: number; }'.


I think this is a hole (I hesitate to say bug not sure if it is by design or not) in weak type checks. A weak type is (according to this PR):





  1. Object types with at least one property

  2. Where all properties are optional

  3. And that do not have a string index signature, number index signature, call signature or construct signature.




However in the implementation of the weak type check for intersections all types of the intersection have to be weak types for the intersection to be a weak type. From compiler code (comments added):



function isWeakType(type: Type): boolean {
if (type.flags & TypeFlags.Object) {
// ....
}
if (type.flags & TypeFlags.Intersection) {
/// All intersection members have to be weak
return every((<IntersectionType>type).types, isWeakType);
}
return false;
}


Since interface Bar {} is not a weak type (as per the first rule, it has no properties) any intersection with it will not be a weak type and will not throw any weak type checking errors. Removing Bar from the intersection will throw errors everywhere you assign an object that has nothing in common with the target .






share|improve this answer
























  • Thanks, great explanation. Also just found that the check at least can be triggered where needed with spreads, foobarFn({...bar}).

    – estus
    Nov 23 '18 at 11:21











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%2f53444963%2fis-not-assignable-to-parameter-of-type-error-with-object-literal%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














Just to make things clear this is not just about excess property checks. Excess property checks come into play when we assign an object literal directly to a location. In your case all the more surprising behavior occurs when assigning an object indirectly, which would normally be allowed under excess property checks.



function foo(o: { bar: number }) { }
foo({ bar: 0, foo: "" }) // error direct assignment
foo({ bar:0, foo: ""} as { bar:0, foo: "" }) // ok indirect


The surprising part, at least to me is thst another check (the weak type check) does not catch this error. Under weak type checks (as described here) if a type has only optional properties, and we try to assign a type that has no properties in common with it we should get an error:



function foo(o: { bar?: number }) { }
foo({ foo: "" }) // error under excess properties: Object literal may only specify known properties, and 'foo' does not exist in type
foo({ foo: ""} as { foo: "" }) // error under weak types: Type '{ foo: ""; }' has no properties in common with type '{ bar?: number; }'.


I think this is a hole (I hesitate to say bug not sure if it is by design or not) in weak type checks. A weak type is (according to this PR):





  1. Object types with at least one property

  2. Where all properties are optional

  3. And that do not have a string index signature, number index signature, call signature or construct signature.




However in the implementation of the weak type check for intersections all types of the intersection have to be weak types for the intersection to be a weak type. From compiler code (comments added):



function isWeakType(type: Type): boolean {
if (type.flags & TypeFlags.Object) {
// ....
}
if (type.flags & TypeFlags.Intersection) {
/// All intersection members have to be weak
return every((<IntersectionType>type).types, isWeakType);
}
return false;
}


Since interface Bar {} is not a weak type (as per the first rule, it has no properties) any intersection with it will not be a weak type and will not throw any weak type checking errors. Removing Bar from the intersection will throw errors everywhere you assign an object that has nothing in common with the target .






share|improve this answer
























  • Thanks, great explanation. Also just found that the check at least can be triggered where needed with spreads, foobarFn({...bar}).

    – estus
    Nov 23 '18 at 11:21
















1














Just to make things clear this is not just about excess property checks. Excess property checks come into play when we assign an object literal directly to a location. In your case all the more surprising behavior occurs when assigning an object indirectly, which would normally be allowed under excess property checks.



function foo(o: { bar: number }) { }
foo({ bar: 0, foo: "" }) // error direct assignment
foo({ bar:0, foo: ""} as { bar:0, foo: "" }) // ok indirect


The surprising part, at least to me is thst another check (the weak type check) does not catch this error. Under weak type checks (as described here) if a type has only optional properties, and we try to assign a type that has no properties in common with it we should get an error:



function foo(o: { bar?: number }) { }
foo({ foo: "" }) // error under excess properties: Object literal may only specify known properties, and 'foo' does not exist in type
foo({ foo: ""} as { foo: "" }) // error under weak types: Type '{ foo: ""; }' has no properties in common with type '{ bar?: number; }'.


I think this is a hole (I hesitate to say bug not sure if it is by design or not) in weak type checks. A weak type is (according to this PR):





  1. Object types with at least one property

  2. Where all properties are optional

  3. And that do not have a string index signature, number index signature, call signature or construct signature.




However in the implementation of the weak type check for intersections all types of the intersection have to be weak types for the intersection to be a weak type. From compiler code (comments added):



function isWeakType(type: Type): boolean {
if (type.flags & TypeFlags.Object) {
// ....
}
if (type.flags & TypeFlags.Intersection) {
/// All intersection members have to be weak
return every((<IntersectionType>type).types, isWeakType);
}
return false;
}


Since interface Bar {} is not a weak type (as per the first rule, it has no properties) any intersection with it will not be a weak type and will not throw any weak type checking errors. Removing Bar from the intersection will throw errors everywhere you assign an object that has nothing in common with the target .






share|improve this answer
























  • Thanks, great explanation. Also just found that the check at least can be triggered where needed with spreads, foobarFn({...bar}).

    – estus
    Nov 23 '18 at 11:21














1












1








1







Just to make things clear this is not just about excess property checks. Excess property checks come into play when we assign an object literal directly to a location. In your case all the more surprising behavior occurs when assigning an object indirectly, which would normally be allowed under excess property checks.



function foo(o: { bar: number }) { }
foo({ bar: 0, foo: "" }) // error direct assignment
foo({ bar:0, foo: ""} as { bar:0, foo: "" }) // ok indirect


The surprising part, at least to me is thst another check (the weak type check) does not catch this error. Under weak type checks (as described here) if a type has only optional properties, and we try to assign a type that has no properties in common with it we should get an error:



function foo(o: { bar?: number }) { }
foo({ foo: "" }) // error under excess properties: Object literal may only specify known properties, and 'foo' does not exist in type
foo({ foo: ""} as { foo: "" }) // error under weak types: Type '{ foo: ""; }' has no properties in common with type '{ bar?: number; }'.


I think this is a hole (I hesitate to say bug not sure if it is by design or not) in weak type checks. A weak type is (according to this PR):





  1. Object types with at least one property

  2. Where all properties are optional

  3. And that do not have a string index signature, number index signature, call signature or construct signature.




However in the implementation of the weak type check for intersections all types of the intersection have to be weak types for the intersection to be a weak type. From compiler code (comments added):



function isWeakType(type: Type): boolean {
if (type.flags & TypeFlags.Object) {
// ....
}
if (type.flags & TypeFlags.Intersection) {
/// All intersection members have to be weak
return every((<IntersectionType>type).types, isWeakType);
}
return false;
}


Since interface Bar {} is not a weak type (as per the first rule, it has no properties) any intersection with it will not be a weak type and will not throw any weak type checking errors. Removing Bar from the intersection will throw errors everywhere you assign an object that has nothing in common with the target .






share|improve this answer













Just to make things clear this is not just about excess property checks. Excess property checks come into play when we assign an object literal directly to a location. In your case all the more surprising behavior occurs when assigning an object indirectly, which would normally be allowed under excess property checks.



function foo(o: { bar: number }) { }
foo({ bar: 0, foo: "" }) // error direct assignment
foo({ bar:0, foo: ""} as { bar:0, foo: "" }) // ok indirect


The surprising part, at least to me is thst another check (the weak type check) does not catch this error. Under weak type checks (as described here) if a type has only optional properties, and we try to assign a type that has no properties in common with it we should get an error:



function foo(o: { bar?: number }) { }
foo({ foo: "" }) // error under excess properties: Object literal may only specify known properties, and 'foo' does not exist in type
foo({ foo: ""} as { foo: "" }) // error under weak types: Type '{ foo: ""; }' has no properties in common with type '{ bar?: number; }'.


I think this is a hole (I hesitate to say bug not sure if it is by design or not) in weak type checks. A weak type is (according to this PR):





  1. Object types with at least one property

  2. Where all properties are optional

  3. And that do not have a string index signature, number index signature, call signature or construct signature.




However in the implementation of the weak type check for intersections all types of the intersection have to be weak types for the intersection to be a weak type. From compiler code (comments added):



function isWeakType(type: Type): boolean {
if (type.flags & TypeFlags.Object) {
// ....
}
if (type.flags & TypeFlags.Intersection) {
/// All intersection members have to be weak
return every((<IntersectionType>type).types, isWeakType);
}
return false;
}


Since interface Bar {} is not a weak type (as per the first rule, it has no properties) any intersection with it will not be a weak type and will not throw any weak type checking errors. Removing Bar from the intersection will throw errors everywhere you assign an object that has nothing in common with the target .







share|improve this answer












share|improve this answer



share|improve this answer










answered Nov 23 '18 at 11:13









Titian Cernicova-DragomirTitian Cernicova-Dragomir

65.3k34361




65.3k34361













  • Thanks, great explanation. Also just found that the check at least can be triggered where needed with spreads, foobarFn({...bar}).

    – estus
    Nov 23 '18 at 11:21



















  • Thanks, great explanation. Also just found that the check at least can be triggered where needed with spreads, foobarFn({...bar}).

    – estus
    Nov 23 '18 at 11:21

















Thanks, great explanation. Also just found that the check at least can be triggered where needed with spreads, foobarFn({...bar}).

– estus
Nov 23 '18 at 11:21





Thanks, great explanation. Also just found that the check at least can be triggered where needed with spreads, foobarFn({...bar}).

– estus
Nov 23 '18 at 11:21




















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%2f53444963%2fis-not-assignable-to-parameter-of-type-error-with-object-literal%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

Ottavio Pratesi

Tricia Helfer

15 giugno