Skip to content

Commit 014845a

Browse files
authored
Merge pull request #15057 from Microsoft/fixExcessPropertyCheck
Fix excess property check
2 parents 11f715c + 69c30a0 commit 014845a

File tree

4 files changed

+86
-10
lines changed

4 files changed

+86
-10
lines changed

src/compiler/checker.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8345,8 +8345,11 @@ namespace ts {
83458345
!t.numberIndexInfo;
83468346
}
83478347

8348-
function isEmptyObjectType(type: Type) {
8349-
return type.flags & TypeFlags.Object && isEmptyResolvedType(resolveStructuredTypeMembers(<ObjectType>type));
8348+
function isEmptyObjectType(type: Type): boolean {
8349+
return type.flags & TypeFlags.Object ? isEmptyResolvedType(resolveStructuredTypeMembers(<ObjectType>type)) :
8350+
type.flags & TypeFlags.Union ? forEach((<UnionType>type).types, isEmptyObjectType) :
8351+
type.flags & TypeFlags.Intersection ? !forEach((<UnionType>type).types, t => !isEmptyObjectType(t)) :
8352+
false;
83508353
}
83518354

83528355
function isEnumTypeRelatedTo(source: EnumType, target: EnumType, errorReporter?: ErrorReporter) {
@@ -8662,14 +8665,8 @@ namespace ts {
86628665
function isKnownProperty(type: Type, name: string, isComparingJsxAttributes: boolean): boolean {
86638666
if (type.flags & TypeFlags.Object) {
86648667
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
8665-
if ((relation === assignableRelation || relation === comparableRelation) &&
8666-
(type === globalObjectType || (!isComparingJsxAttributes && isEmptyObjectType(resolved)))) {
8667-
return true;
8668-
}
8669-
else if (resolved.stringIndexInfo || (resolved.numberIndexInfo && isNumericLiteralName(name))) {
8670-
return true;
8671-
}
8672-
else if (getPropertyOfType(type, name) || (isComparingJsxAttributes && !isUnhyphenatedJsxName(name))) {
8668+
if (resolved.stringIndexInfo || resolved.numberIndexInfo && isNumericLiteralName(name) ||
8669+
getPropertyOfType(type, name) || isComparingJsxAttributes && !isUnhyphenatedJsxName(name)) {
86738670
// For JSXAttributes, if the attribute has a hyphenated name, consider that the attribute to be known.
86748671
return true;
86758672
}
@@ -8687,6 +8684,10 @@ namespace ts {
86878684
function hasExcessProperties(source: FreshObjectLiteralType, target: Type, reportErrors: boolean): boolean {
86888685
if (maybeTypeOfKind(target, TypeFlags.Object) && !(getObjectFlags(target) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) {
86898686
const isComparingJsxAttributes = !!(source.flags & TypeFlags.JsxAttributes);
8687+
if ((relation === assignableRelation || relation === comparableRelation) &&
8688+
(target === globalObjectType || (!isComparingJsxAttributes && isEmptyObjectType(target)))) {
8689+
return false;
8690+
}
86908691
for (const prop of getPropertiesOfObjectType(source)) {
86918692
if (!isKnownProperty(target, prop.name, isComparingJsxAttributes)) {
86928693
if (reportErrors) {
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
tests/cases/compiler/excessPropertyCheckWithEmptyObject.ts(4,23): error TS2304: Cannot find name 'window'.
2+
tests/cases/compiler/excessPropertyCheckWithEmptyObject.ts(4,58): error TS2345: Argument of type '{ value: string; readonly: boolean; }' is not assignable to parameter of type 'PropertyDescriptor & ThisType<any>'.
3+
Object literal may only specify known properties, and 'readonly' does not exist in type 'PropertyDescriptor & ThisType<any>'.
4+
tests/cases/compiler/excessPropertyCheckWithEmptyObject.ts(9,30): error TS2322: Type '{ y: number; }' is not assignable to type 'A & ThisType<any>'.
5+
Object literal may only specify known properties, and 'y' does not exist in type 'A & ThisType<any>'.
6+
tests/cases/compiler/excessPropertyCheckWithEmptyObject.ts(14,34): error TS2322: Type '{ y: string; }' is not assignable to type 'Empty & { x: number; }'.
7+
Object literal may only specify known properties, and 'y' does not exist in type 'Empty & { x: number; }'.
8+
9+
10+
==== tests/cases/compiler/excessPropertyCheckWithEmptyObject.ts (4 errors) ====
11+
// Repro from #14910
12+
13+
// Excess property error expected here
14+
Object.defineProperty(window, "prop", { value: "v1.0.0", readonly: false });
15+
~~~~~~
16+
!!! error TS2304: Cannot find name 'window'.
17+
~~~~~~~~~~~~~~~
18+
!!! error TS2345: Argument of type '{ value: string; readonly: boolean; }' is not assignable to parameter of type 'PropertyDescriptor & ThisType<any>'.
19+
!!! error TS2345: Object literal may only specify known properties, and 'readonly' does not exist in type 'PropertyDescriptor & ThisType<any>'.
20+
21+
interface A { x?: string }
22+
23+
// Excess property error expected here
24+
let a: A & ThisType<any> = { y: 10 };
25+
~~~~~
26+
!!! error TS2322: Type '{ y: number; }' is not assignable to type 'A & ThisType<any>'.
27+
!!! error TS2322: Object literal may only specify known properties, and 'y' does not exist in type 'A & ThisType<any>'.
28+
29+
interface Empty {}
30+
31+
// Excess property error expected here
32+
let x: Empty & { x: number } = { y: "hello" };
33+
~~~~~~~~~~
34+
!!! error TS2322: Type '{ y: string; }' is not assignable to type 'Empty & { x: number; }'.
35+
!!! error TS2322: Object literal may only specify known properties, and 'y' does not exist in type 'Empty & { x: number; }'.
36+
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
//// [excessPropertyCheckWithEmptyObject.ts]
2+
// Repro from #14910
3+
4+
// Excess property error expected here
5+
Object.defineProperty(window, "prop", { value: "v1.0.0", readonly: false });
6+
7+
interface A { x?: string }
8+
9+
// Excess property error expected here
10+
let a: A & ThisType<any> = { y: 10 };
11+
12+
interface Empty {}
13+
14+
// Excess property error expected here
15+
let x: Empty & { x: number } = { y: "hello" };
16+
17+
18+
//// [excessPropertyCheckWithEmptyObject.js]
19+
// Repro from #14910
20+
// Excess property error expected here
21+
Object.defineProperty(window, "prop", { value: "v1.0.0", readonly: false });
22+
// Excess property error expected here
23+
var a = { y: 10 };
24+
// Excess property error expected here
25+
var x = { y: "hello" };
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// Repro from #14910
2+
3+
// Excess property error expected here
4+
Object.defineProperty(window, "prop", { value: "v1.0.0", readonly: false });
5+
6+
interface A { x?: string }
7+
8+
// Excess property error expected here
9+
let a: A & ThisType<any> = { y: 10 };
10+
11+
interface Empty {}
12+
13+
// Excess property error expected here
14+
let x: Empty & { x: number } = { y: "hello" };

0 commit comments

Comments
 (0)