Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Argument of type X is not assignable to parameter of type Y, but then assignable. - Misleading error #50438

Open
ackvf opened this issue Aug 25, 2022 · 3 comments
Labels
Cursed? It's likely this is extremely difficult to fix without making something else much, much worse Experience Enhancement Noncontroversial enhancements Help Wanted You can do this Suggestion An idea for TypeScript
Milestone

Comments

@ackvf
Copy link

ackvf commented Aug 25, 2022

Bug Report

🔎 Search Terms

Argument of type is not assignable to parameter of type, object properties overlap

🕗 Version & Regression Information

  • This is the behavior in every version I tried, and I reviewed the FAQ for entries.

The closest I could find in the FAQ is this https://github.com/Microsoft/TypeScript/wiki/FAQ#why-are-all-types-assignable-to-empty-interfaces

Type { color: "red", margin: 1 } has the required member of Props, so it may correctly be evaluated by TS as fitting, however, the error in the first place is then misleading.

⏯ Playground Link

same result with 4.8.0-beta

Playground link with relevant code

💻 Code

export const is = <TargetType,>() => <T extends TargetType>(arg: T): T => arg

type Props = { color: string }

let x = is<Props>()({ margin: 1 }) // ERROR: Argument of type '{ margin: number; }' is not assignable to parameter of type 'Props'. Object literal may only specify known properties, and 'margin' does not exist in type 'Props'.(2345)
let y = is<Props>()({ color: "red", margin: 1 }) // margin .... OK

Since {margin: 1} is clearly tolerated, the error given is misleading as the actual error is not in the presence of margin but in the absence of color, which is a property required by the Props type.

🙁 Actual behavior

Argument of type '{ margin: number; }' is not assignable to parameter of type 'Props'. Object literal may only specify known properties, and 'margin' does not exist in type 'Props'.(2345)

🙂 Expected behavior

Argument of type '{ margin: number; }' is not assignable to parameter of type 'Props'. Object literal does not specify properties required by 'Props'.(2345)

@RyanCavanaugh RyanCavanaugh added Suggestion An idea for TypeScript Help Wanted You can do this Experience Enhancement Noncontroversial enhancements Cursed? It's likely this is extremely difficult to fix without making something else much, much worse labels Aug 26, 2022
@RyanCavanaugh RyanCavanaugh added this to the Backlog milestone Aug 26, 2022
@RyanCavanaugh
Copy link
Member

This is a bad user experience but I'm not sure how we'd fix it. The algorithm as it stands during generic inference is:

  1. Collect all the candidates
  2. Find the best candidate among them (this is irrelevant since there is only one)
  3. See if that type matches the constraint
  4. If not, process the call as if the type parameter was actually the constraint

Critically, at step 4, it is not guaranteed that an error will be produced. An example would be

// Retains n: number property
const m = foo({ someObj: { keep: true, n: 0 } });
//    ^?

// Not an error, but the candidate did violate the constraint
const n = foo({ someObj: "w/e" });
//    ^?

@ackvf
Copy link
Author

ackvf commented Aug 27, 2022

If I recall correctly, more often than not I could just simply specify any arbitrary extra properties on an object without raising this error Object literal may only specify known properties. Actually, now I can't even think of such situation where specifying them would be completely forbidden.

  • So I wonder how helpful this message is at all?
  • Wouldn't it be more helpful (in more cases) to say the other message?
  • Why not both?

New suggestion:

Argument of type '{ margin: number; }' is not assignable to parameter of type 'Props'. Object literal may only specify known properties, and 'margin' does not exist in type 'Props' or Object literal does not specify properties required by 'Props'..(2345) ¯\_(ツ)_/¯


This is even worse

type Props = { color: string }
let a = is<Props>()({ color: "red", margin: 1 }) // margin .... OK

type Props = { color: string, bright: boolean }
let x = is<Props>()({ color: "red", margin: 1 }) // ERROR: Argument of type '{ margin: number; }' is not assignable to parameter of type 'Props'. Object literal may only specify known properties, and 'margin' does not exist in type 'Props'.(2345)
let y = is<Props>()({ color: "red"})             // ERROR: Argument of type '{ color: string; }' is not assignable to parameter of type 'Props'. Property 'bright' is missing in type '{ color: string; }' but required in type 'Props'.(2345)
let z = is<Props>()({ color: "red", bright: true, margin: 1 }) // margin .... OK

@ackvf
Copy link
Author

ackvf commented Sep 1, 2022

The point above is that in x the margin is not allowed, but in z, it is allowed. Hence making the error message in x unreliable and misleading.

On the other hand, this behaves properly. playground

let a: Props = { color: 'red'}                          // ERROR: Property 'bright' is missing in type '{ color: string; }' but required in type 'Props'.(2741)
let b: Props = { color: 'red', margin: 1}               // ERROR: Type '{ color: string; margin: number; }' is not assignable to type 'Props'. Object literal may only specify known properties, and 'margin' does not exist in type 'Props'.(2322)
let c: Props = { color: 'red', bright: true, margin: 2} // ERROR: Type '{ color: string; margin: number; }' is not assignable to type 'Props'. Object literal may only specify known properties, and 'margin' does not exist in type 'Props'.(2322)
let d: Props = { color: 'red', bright: true }           // OK

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Cursed? It's likely this is extremely difficult to fix without making something else much, much worse Experience Enhancement Noncontroversial enhancements Help Wanted You can do this Suggestion An idea for TypeScript
Projects
None yet
Development

No branches or pull requests

2 participants