GV

GeezerVison

The World Wide Web, restored to 800 pixels of glory.

Home|About|Gaming cabinet|Archives|How it works|Starter sites

On this site

  • Sign in
  • ActionsCI/CD and automation
  • CodespacesCloud development environments
  • All Features
  • Documentation
  • Training
  • Certifications
  • Build with Copilot
  • Customize Copilot
  • Integrations
  • Trending repos
  • GitHub Sponsors
  • Developer articles
  • Enterprises

This view

  • Original page
  • New address

GeezerVison is showing github.com — Strict object literal assignment checking by ahejlsberg · Pull Request #3823 · microsoft/TypeScript

Conversation

@ahejlsberg

@ahejlsberg Anders Hejlsberg (ahejlsberg) commented Jul 11, 2015

Copy link
Copy Markdown
Member

This PR implements stricter object literal assignment checks for the purpose of catching excess or misspelled properties. The PR implements the suggestions in #3755. Specifically:

  • Every object literal is initially considered "fresh".
  • When a fresh object literal is assigned to a variable or passed for a parameter of a non-empty target type, it is an error for the object literal to specify properties that don't exist in the target type.
  • Freshness disappears in a type assertion or when the type of an object literal is widened.

Some examples:

var x: { foo: number };
x = { foo: 1, baz: 2 };  // Error, excess property `baz`

var y: { foo: number, bar?: number };
y = { foo: 1, baz: 2 };  // Error, excess or misspelled property `baz`

The rationale for the errors above is that since the fresh types of the object literals are never captured in variables, static knowledge of the excess or misspelled properties should not be silently lost. No errors occur when the fresh types are captured in variables:

var x: { foo: number };
x1 = { foo: 1, baz: 2 };
x = x1;

var y: { foo: number, bar?: number };
y1 = { foo: 1, baz: 2 };
y = y1;

A type can include an index signature to explicitly indicate that excess properties are permitted:

var x: { foo: number, [x: string]: any };
x = { foo: 1, baz: 2 };  // Ok, `baz` matched by index signature

UPDATE 1: This PR also tightens certain parts of the implementation of union types. Specifically:

  • We no longer perform subtype reduction on union types but instead perform a non-lossy deduplication of the constituent type set.
  • The process for determining call and constuct signatures of a union type is now less restrictive.
  • Similar to intersection types, union types now preserve order of the constituent types.

These changes are detailed in the comment thread below.

UPDATE 2: The -suppressExcessPropertyErrors compiler option can be used to disable strict object literal assignment checking. See #4484.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

WanderWang, ulrichb, AJamesPhillips, SlurpTheo, and mnpenner reacted with thumbs up emoji
All reactions
  • 👍 5 reactions
Anders Hejlsberg (ahejlsberg) added 3 commits July 8, 2015 19:04
@ahejlsberg
Object literals can only have properties that exist in contextual type
cd0d3ba
@ahejlsberg
Switch to assignability check and fix compiler bugs found by check
11aecee
@ahejlsberg
Accepting new baselines
3fe7591
@msftclas Microsoft Contribution License Agreements (msftclas) added the cla-not-required label Jul 11, 2015
@ahejlsberg
Support union and intersection types in checks
f57991e
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Jul 11, 2015
Error when 'extra' properties appear in object literals #3755
Closed
RyanCavanaugh
RyanCavanaugh reviewed Jul 11, 2015
View reviewed changes
Comment thread tests/baselines/reference/arrayLiteralTypeInference.errors.txt

@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) Jul 11, 2015

Copy link
Copy Markdown
Member

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I think this should be trueness -- it doesn't look like this test was intended to fail. Another 🏆 for this change's effectiveness.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@shamhub Programming (shamhub) May 2, 2018

Copy link
Copy Markdown

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

https://stackoverflow.com/questions/50143250/how-excess-property-check-helps

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@RyanCavanaugh

Ryan Cavanaugh (RyanCavanaugh) commented Jul 11, 2015

Copy link
Copy Markdown
Member

👍 . Pulled it down and tried it out for a while -- behavior seems perfect.

I was trying to find an idiomatic way to remove freshness without casting to any. Going through function identity<T>(x: T): T works but has some runtime impact. Any ideas?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Jul 12, 2015

Copy link
Copy Markdown
Member Author

The only way to remove freshness that doesn't generate additional code is a type assertion. I think this is consistent with other situations in which you want to override the type checker. I'm not sure there is a need for a special idiom to defeat the checks associated with freshness.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@JsonFreeman

Jason Freeman (JsonFreeman) commented Jul 13, 2015

Copy link
Copy Markdown
Contributor

I agree that a type assertion is a reasonable escape hatch. You also do not have to cast to any, you can cast to a better type if you have one.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

JsonFreeman
JsonFreeman reviewed Jul 13, 2015
View reviewed changes
Comment thread src/compiler/types.ts

@JsonFreeman Jason Freeman (JsonFreeman) Jul 13, 2015

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Please define fresh object literal in the comment.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@JsonFreeman Jason Freeman (JsonFreeman) Jul 13, 2015

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Why do we need a new flag here? This is set in the same place as ObjectLiteral. And when you get the regular type, you turn off FreshObjectLiteral, but not ObjectLiteral, and I don't really understand why. So a regular type is allowed to have excess properties, but it still does not need to have all the optional properties of the target in a subtype check?

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ahejlsberg Anders Hejlsberg (ahejlsberg) Jul 13, 2015

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Well, it's related to the issue of removing freshness after the first assignment, but still remembering that the source was an object literal. It may be that we can combine the two if we give up on the first assignment bit.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@JsonFreeman Jason Freeman (JsonFreeman) Jul 13, 2015

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Yup, I think we should give up the first assignment bit. I think it is a strange rule.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@ahejlsberg Anders Hejlsberg (ahejlsberg) Jul 13, 2015

Copy link
Copy Markdown
Member Author

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

So, I don't think we can get rid of the new flag. We use TypeFlag.ObjectLiteral to indicate that a type originated in an object literal and may contain null or undefined types. We can't turn off that flag unless we also widen the type. Yet, in type assertions and during subtype reduction we want to turn off freshness but not widen.

That said, we could still drop the assignment rule. They're really orthogonal issues.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@JsonFreeman Jason Freeman (JsonFreeman) Jul 14, 2015

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Yes, let's drop the assignment rule.

I understand what you mean about TypeFlags.ObjectLiteral, but I still think we can use it. We actually use ContainsUndefinedOrNull and ContainsObjectLiteral for what you are talking about, not ObjectLiteral directly. For type assertions, I think it's fine to widen, we already do in the downcast direction, and I think it's fine to do it for the upcast (it uses assignability so we should be fine). For creation of a union type, we already give it TypeFlags.Union plus all the widening flags (which do not include ObjectLiteral anyway). So all the flags that getWidenedType checks for would still be there.

So I think it is safe to remove it.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@JsonFreeman Jason Freeman (JsonFreeman) Jul 14, 2015

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

To elaborate about the type assertion case, widening would essentially do two things:

  1. null and undefined become any. They are bottom types anyway, so changing them to any should have no effect.
  2. The object literal is changed with respect to the optional-properties-being-required rule. This rule only applies to subtype, and type assertions use assignability.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@JsonFreeman Jason Freeman (JsonFreeman) Jul 14, 2015

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

I admit it's still possible that I missed something, but if I did, I'd like to understand it before allowing us to have two flags that sound really similar and are easy to confuse.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@JsonFreeman

Jason Freeman (JsonFreeman) commented Jul 13, 2015

Copy link
Copy Markdown
Contributor

When we discussed this, we had some thoughts about indexers. Namely if the object type has an indexer, we want to allow excess properties to be provided, since the object might behave as a map. What is the reason for abandoning that idea? Is this going to limit the usefulness of indexers with object literals?

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@JsonFreeman

Jason Freeman (JsonFreeman) commented Jul 13, 2015

Copy link
Copy Markdown
Contributor

Where is it that an object literal type loses its freshness? I don't see any place in the code where inferring an object type for a variable causes us to infer the regular type.

To clarify, I would assume this "freshness" gets lost when we widen an object literal type. This is what we do to indicate for example that for subtype, optional properties in the target must be declared in the source, unless the source is a "fresh" (unwidened) object literal. So I am looking for similar logic here.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@JsonFreeman

Jason Freeman (JsonFreeman) commented Jul 13, 2015

Copy link
Copy Markdown
Contributor

I got my indexer question answered.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

JsonFreeman
JsonFreeman reviewed Jul 13, 2015
View reviewed changes
Comment thread src/compiler/checker.ts Outdated

@JsonFreeman Jason Freeman (JsonFreeman) Jul 13, 2015

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Ah, so you do these checks on the target side as opposed to when contextually typing the object literal. Actually, this is probably a good thing, because not every assignability check is guaranteed to be accompanied by contextual typing. So if the object did not get contextually typed, the assignment would still be allowed, which is good I think.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions

@JsonFreeman Jason Freeman (JsonFreeman) Jul 13, 2015

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

But I would add some comments explaining the rationale behind these heuristics.

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
@ahejlsberg
Addressing CR feedback
aa26980
JsonFreeman
JsonFreeman reviewed Jul 13, 2015
View reviewed changes
Comment thread src/compiler/checker.ts

@JsonFreeman Jason Freeman (JsonFreeman) Jul 13, 2015

Copy link
Copy Markdown
Contributor

There was a problem hiding this comment.

Choose a reason for hiding this comment

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

Thanks. This makes sense now

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

All reactions
Anders Hejlsberg (ahejlsberg) added 3 commits July 13, 2015 11:15
@ahejlsberg
Ignore freshness in subtype reduction / Treat Object as {}
d78fa18
@ahejlsberg
Fixing bug in test
2eca3d5
@ahejlsberg
Accepting new baselines
c7b0732
@danquirk

Dan Quirk (danquirk) commented Jul 13, 2015

Copy link
Copy Markdown
Member

Note this is a breaking change. We should probably put this in a widely distributed beta before unleashing it as a default in 1.6 or some future version.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@ahejlsberg

Anders Hejlsberg (ahejlsberg) commented Jul 13, 2015

Copy link
Copy Markdown
Member Author

Dan Quirk (@danquirk) Agreed.

All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

@sophiajt

Sophia J. Turner (sophiajt) commented Jul 13, 2015

Copy link
Copy Markdown
Contributor

We talked about this a bit today in-person. I'm not sure I'm keen on co-opting object literals to mean this, but I think my real discomfort is that, ideally, interfaces should be able to describe any type. They're the fundamental building block of the type system*.

Because of this, I think the natural place is to describe this first in the interface first. Strictness is opt-in there, and would not break code.

I get that there is a lot of interest in putting this in and catching bugs, but I'm not yet seeing the advantage of taking the breaking change over our philosophy of "interface is the foundation of the type system"

That said, I could be swayed...

  • - there are a couple corner cases where this isn't true, but arguably we may want to fix those over time.
All reactions

Sorry, something went wrong.

Uh oh!

There was an error while loading. Please reload this page.

Miško Hevery (mhevery) added a commit to mhevery/angular that referenced this pull request Aug 13, 2015
@mhevery
chore: Remove IRequestOptions / IResponseOptions
7f2b413
BREAKING CHANGE:

Reasons:

1) Interfaces should not start with letter ‘I’
2) Interfaces do not help with mistype properties, but literal types do.
   - microsoft/TypeScript#3823
   - https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#strict-object-literal-assignment-checking
Miško Hevery (mhevery) added a commit to angular/angular that referenced this pull request Aug 13, 2015
@mhevery
chore: Remove IRequestOptions / IResponseOptions
0b8b3fb
BREAKING CHANGE:

Reasons:

1) Interfaces should not start with letter ‘I’
2) Interfaces do not help with mistype properties, but literal types do.
   - microsoft/TypeScript#3823
   - https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#strict-object-literal-assignment-checking
Miško Hevery (mhevery) added a commit to angular/angular that referenced this pull request Aug 13, 2015
@mhevery
chore: Remove IRequestOptions / IResponseOptions
512340e
BREAKING CHANGE:

Reasons:

1) Interfaces should not start with letter ‘I’
2) Interfaces do not help with mistype properties, but literal types do.
   - microsoft/TypeScript#3823
   - https://github.com/Microsoft/TypeScript/wiki/Breaking-Changes#strict-object-literal-assignment-checking
Robert Knight (robertknight) added a commit to robertknight/passcards that referenced this pull request Aug 16, 2015
@robertknight
Fix compatibility with TS 1.6 object literal typing
a1bc33b
TS 1.6 requires that object literals assigned
to variables or parameters with an interface typing
may only contain properties that are listed in the interface.

See microsoft/TypeScript#3823
for details.

This mostly affected React component props interfaces
which did not extend react.Props so were missing the common
key, ref and children props.
@jbondc Jon (jbondc) mentioned this pull request Aug 18, 2015
What do we do about object literals that make use of structural subtyping in 1.6? #4234
Closed
This was referenced Aug 27, 2015
Adding -suppressExcessPropertyErrors compiler option #4484
Merged
Union subtype reduction #4537
Merged
@mhegazy Mohamed Hegazy (mhegazy) mentioned this pull request Sep 3, 2015
Union of function signatures cannot be invoked because it lacks a call signature #4612
Closed
@Kuniwak Yuki Kokubun (Kuniwak) mentioned this pull request Sep 3, 2015
CI failure on TypeScript 1.6 Beta palantir/tslint#634
Closed
@kimamula Kenji Imamula (kimamula) mentioned this pull request Sep 6, 2015
add Event3 and Event4 kimamula/ts-eventemitter#1
Closed
@jods4 jods (jods4) mentioned this pull request Sep 8, 2015
TS 1.6: strict object literal assignment aurelia/router#208
Closed
@fredgalvao Frederico Galvão (fredgalvao) mentioned this pull request Sep 20, 2015
Make definitions work nicely with third-party promise implementations AGBrown/pouchdb.d.ts#18
Closed
@kitsonk Kitson Kelly (kitsonk) mentioned this pull request Dec 20, 2015
problem with interfaces #6175
Closed
@ewinslow Evan Winslow (ewinslow) mentioned this pull request Jan 5, 2016
Stricter interfaces/literal types #6352
Closed
@yortus Troy Gerwien (yortus) mentioned this pull request Feb 8, 2016
Typescript: Pass an extra parameter #6963
Closed
@malibuzios malibuzios mentioned this pull request May 19, 2016
show error when use difference two way call function. #8682
Closed
@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) mentioned this pull request Jan 12, 2017
Excess properties in object literals are allowed #13444
Closed
This was referenced Feb 28, 2017
Typed 'this' in object literal methods #14141
Merged
Possible bug: Hybrid between two interfaces of a union passes type checking #14383
Closed
@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) mentioned this pull request Mar 10, 2017
Unexpected behavior for "keyof" type-check #14576
Closed
@ahejlsberg Anders Hejlsberg (ahejlsberg) mentioned this pull request Mar 12, 2017
Empty interface allow any object? #14606
Closed
@olegdunkan olegdunkan mentioned this pull request Sep 10, 2017
[BUG] Issue with union and literal types #18365
Closed
@kitsonk Kitson Kelly (kitsonk) mentioned this pull request Oct 25, 2017
Tuple types accepting incorrect data #19463
Closed
@jdmunro James Munro (jdmunro) mentioned this pull request Mar 29, 2018
Function unexpectedly allows returning arguments object directly if it's a superset of the return type #22982
Closed
@RyanCavanaugh Ryan Cavanaugh (RyanCavanaugh) mentioned this pull request May 21, 2018
Index signature pointing to any is too relaxed #24083
Closed
@microsoft Microsoft (microsoft) locked and limited conversation to collaborators Jul 31, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Reviewers

1 more reviewer
@shamhub Programming (shamhub) shamhub left review comments
Reviewers whose approvals may not affect merge requirements

Uh oh!

There was an error while loading. Please reload this page.

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

Uh oh!

There was an error while loading. Please reload this page.

11 participants

@ahejlsberg @RyanCavanaugh @JsonFreeman @danquirk @sophiajt @mihailik @joewood @spion @DanielRosenwasser @shamhub @msftclas
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
You are visitor 01402503 — GeezerVison does not store the pages you look up.
Best viewed with Netscape Navigator 4.7 or Internet Explorer 6 at 800×600. No cookies required.