-
Notifications
You must be signed in to change notification settings - Fork 714
Types with extra properties are never subtypes of fresh object literals #1759
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
Conversation
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.
Pull Request Overview
This PR modifies type relationship checking to enforce that types with extra properties are never subtypes of fresh object literals. This ensures proper asymmetric subtyping relationships where fresh object literal types are subtypes of regular object types with optional properties, but not vice-versa.
Key changes:
- Updated property relationship checking logic to only check for excess properties when the target is a fresh object literal
- Removed the undefined type checking condition that was preventing proper subtype relationships
- Added comprehensive test coverage for the fresh object literal subtype behavior
Reviewed Changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
File | Description |
---|---|
testdata/tests/cases/compiler/freshObjectLiteralSubtype.ts |
New test case demonstrating the fresh object literal subtype behavior and reproducing the issue from #1742 |
testdata/baselines/reference/compiler/freshObjectLiteralSubtype.types |
Expected type checking output for the new test case |
testdata/baselines/reference/compiler/freshObjectLiteralSubtype.symbols |
Expected symbol resolution output for the new test case |
internal/checker/relater.go |
Core logic change to restrict excess property checking to fresh object literals only |
if target.objectFlags&ObjectFlagsFreshLiteral != 0 { | ||
for _, sourceProp := range excludeProperties(r.c.getPropertiesOfType(source), excludedProperties) { | ||
if r.c.getPropertyOfObjectType(target, sourceProp.Name) == nil { | ||
sourceType := r.c.getTypeOfSymbol(sourceProp) | ||
if sourceType.flags&TypeFlagsUndefined == 0 { | ||
if reportErrors { | ||
r.reportError(diagnostics.Property_0_does_not_exist_on_type_1, r.c.symbolToString(sourceProp), r.c.TypeToString(target)) | ||
} | ||
return TernaryFalse | ||
if reportErrors { | ||
r.reportError(diagnostics.Property_0_does_not_exist_on_type_1, r.c.symbolToString(sourceProp), r.c.TypeToString(target)) | ||
} | ||
return TernaryFalse | ||
} | ||
} | ||
} |
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.
The excess property checking logic has been simplified but the removal of the undefined type check may be too broad. The previous logic if sourceType.flags&TypeFlagsUndefined == 0
ensured that undefined properties weren't considered excess properties, which might be important for optional property handling. Consider whether this check should be preserved to maintain correct behavior for optional properties.
Copilot uses AI. Check for mistakes.
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.
This is intended.
This PR modifies type relationship checking such that types with extra properties are never subtypes of fresh object literals. This in turn ensures that fresh object literal types are subtypes of regular object types with optional properties that aren't specified in the fresh object literal types. For example, with this PR the fresh object literal type
{ valid: boolean }
is a subtype of the regular type{ valid: boolean; msg?: undefined; }
, but not vice-versa, where previously both were subtypes of each other.Fixes #1742.