Skip to content

Improve excess property checks #28854

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

Merged
merged 4 commits into from
Dec 5, 2018
Merged

Improve excess property checks #28854

merged 4 commits into from
Dec 5, 2018

Conversation

ahejlsberg
Copy link
Member

@ahejlsberg ahejlsberg commented Dec 4, 2018

With this PR we improve the logic for determining when to perform excess property checks. Specifically, we now perform excess property checks when the source type is a fresh object literal and the target type is not an empty object type (as determined by isEmptyObjectType) and is an excess property check target. An excess property check target is defined as

  • an object type with no computed properties,
  • the predefined object type,
  • a union type containing at least one excess property check target, or
  • an intersection type containing only excess property check targets.

Fixes #28752 (in that the error messages are less confusing).

@@ -40,7 +50,7 @@ tests/cases/compiler/objectLiteralExcessProperties.ts(33,27): error TS2322: Type
var b2: Book | string = { foreward: "nope" };
~~~~~~~~~~~~~~~~
!!! error TS2322: Type '{ foreward: string; }' is not assignable to type 'string | Book'.
!!! error TS2322: Object literal may only specify known properties, and 'foreward' does not exist in type 'string | Book'.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note: in this or another PR, we should make these both but

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably most correct to have two separate sentences, as the subjects of the clauses are not well-connected. Object literal may only specify known properties. 'foreward' does not exist in type 'string | Book'. Two separate statements of fact - there is no real relationship between them, so a conjunction feels a bit extraneous to me. 🚲 🏚

I call in @sandersn as our resident linguist to weigh in.

@DanielRosenwasser
Copy link
Member

Looks good, but it's not clear why we don't want to dive into conditional types.

// No excess property checks on generic types
const obj1: T = { name: "test" };
~~~~
!!! error TS2322: Type '{ name: string; }' is not assignable to type 'T'.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Side note: I've seen a couple issues opened where people don't really understand constrained generics well and are surprised an assignment like this fails and don't "get" the error. A little more elaboration could help here (ie, when assignment to a bare type parameter fails), like A is assignable to T's constraint B, but not T itself., just to indicate that "we looked at what you're thinking of, and that isn't what's going on here".

Unrelated to this PR, though. Just a remark. @DanielRosenwasser you agree?

!!! error TS2322: Type '{ name: string; prop: boolean; }' is not assignable to type 'T'.
// Excess property checks only on non-generic parts of unions
const obj3: T | { prop: boolean } = { name: "test", prop: true };
~~~~~~~~~~~~
Copy link
Member

@weswigham weswigham Dec 5, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes sense in an assignment context, but

declare function foo<T>(x: T | { prop: boolean }): T | boolean;

foo({prop: true, name: "test"}); // probably shouldn't be excess, trivially satisfies `T`

for calls it seems a little jank, perhaps? The union case just seems presumptuous to me, I guess.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does the excess error happen before inference of T to {prop: true, name: string}?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@weswigham As @jack-williams points out, there's no error in your example because we infer and substitute a type for T before the assignment check.

@ahejlsberg ahejlsberg merged commit 07dbc56 into master Dec 5, 2018
@ahejlsberg ahejlsberg deleted the fixExcessPropertyChecks branch December 5, 2018 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants