Skip to content

Proof-of-Concept: Excess property checking for nested intersection and union #23798

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

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
467 changes: 394 additions & 73 deletions src/compiler/checker.ts

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
tests/cases/conformance/types/union/contextualTypeWithUnionTypeMembers.ts(42,5): error TS2322: Type '{ commonPropertyType: string; commonMethodType: (a: string) => string; commonMethodWithTypeParameter: (a: number) => number; methodOnlyInI1: (a: string) => string; propertyOnlyInI1: string; methodOnlyInI2: (a: string) => string; propertyOnlyInI2: string; }' is not assignable to type 'I1<number> | I2<number>'.
tests/cases/conformance/types/union/contextualTypeWithUnionTypeMembers.ts(52,5): error TS2322: Type '(I1<number> | I2<number> | { commonPropertyType: string; commonMethodType: (a: string) => string; commonMethodWithTypeParameter: (a: number) => number; methodOnlyInI1: (a: string) => string; propertyOnlyInI1: string; methodOnlyInI2: (a: string) => string; propertyOnlyInI2: string; })[]' is not assignable to type '(I1<number> | I2<number>)[]'.
Type 'I1<number> | I2<number> | { commonPropertyType: string; commonMethodType: (a: string) => string; commonMethodWithTypeParameter: (a: number) => number; methodOnlyInI1: (a: string) => string; propertyOnlyInI1: string; methodOnlyInI2: (a: string) => string; propertyOnlyInI2: string; }' is not assignable to type 'I1<number> | I2<number>'.
Type '{ commonPropertyType: string; commonMethodType: (a: string) => string; commonMethodWithTypeParameter: (a: number) => number; methodOnlyInI1: (a: string) => string; propertyOnlyInI1: string; methodOnlyInI2: (a: string) => string; propertyOnlyInI2: string; }' is not assignable to type 'I1<number> | I2<number>'.


==== tests/cases/conformance/types/union/contextualTypeWithUnionTypeMembers.ts (2 errors) ====
//When used as a contextual type, a union type U has those members that are present in any of
// its constituent types, with types that are unions of the respective members in the constituent types.
interface I1<T> {
commonMethodType(a: string): string;
commonPropertyType: string;
commonMethodWithTypeParameter(a: T): T;

methodOnlyInI1(a: string): string;
propertyOnlyInI1: string;
}
interface I2<T> {
commonMethodType(a: string): string;
commonPropertyType: string;
commonMethodWithTypeParameter(a: T): T;

methodOnlyInI2(a: string): string;
propertyOnlyInI2: string;
}

// Let S be the set of types in U that has a property P.
// If S is not empty, U has a property P of a union type of the types of P from each type in S.
var i1: I1<number>;
var i2: I2<number>;
var i1Ori2: I1<number> | I2<number> = i1;
var i1Ori2: I1<number> | I2<number> = i2;
var i1Ori2: I1<number> | I2<number> = { // Like i1
commonPropertyType: "hello",
commonMethodType: a=> a,
commonMethodWithTypeParameter: a => a,

methodOnlyInI1: a => a,
propertyOnlyInI1: "Hello",
};
var i1Ori2: I1<number> | I2<number> = { // Like i2
commonPropertyType: "hello",
commonMethodType: a=> a,
commonMethodWithTypeParameter: a => a,

methodOnlyInI2: a => a,
propertyOnlyInI2: "Hello",
};
var i1Ori2: I1<number> | I2<number> = { // Like i1 and i2 both
~~~~~~
!!! error TS2322: Type '{ commonPropertyType: string; commonMethodType: (a: string) => string; commonMethodWithTypeParameter: (a: number) => number; methodOnlyInI1: (a: string) => string; propertyOnlyInI1: string; methodOnlyInI2: (a: string) => string; propertyOnlyInI2: string; }' is not assignable to type 'I1<number> | I2<number>'.
commonPropertyType: "hello",
commonMethodType: a=> a,
commonMethodWithTypeParameter: a => a,
methodOnlyInI1: a => a,
propertyOnlyInI1: "Hello",
methodOnlyInI2: a => a,
propertyOnlyInI2: "Hello",
};

var arrayI1OrI2: Array<I1<number> | I2<number>> = [i1, i2, { // Like i1
~~~~~~~~~~~
!!! error TS2322: Type '(I1<number> | I2<number> | { commonPropertyType: string; commonMethodType: (a: string) => string; commonMethodWithTypeParameter: (a: number) => number; methodOnlyInI1: (a: string) => string; propertyOnlyInI1: string; methodOnlyInI2: (a: string) => string; propertyOnlyInI2: string; })[]' is not assignable to type '(I1<number> | I2<number>)[]'.
!!! error TS2322: Type 'I1<number> | I2<number> | { commonPropertyType: string; commonMethodType: (a: string) => string; commonMethodWithTypeParameter: (a: number) => number; methodOnlyInI1: (a: string) => string; propertyOnlyInI1: string; methodOnlyInI2: (a: string) => string; propertyOnlyInI2: string; }' is not assignable to type 'I1<number> | I2<number>'.
!!! error TS2322: Type '{ commonPropertyType: string; commonMethodType: (a: string) => string; commonMethodWithTypeParameter: (a: number) => number; methodOnlyInI1: (a: string) => string; propertyOnlyInI1: string; methodOnlyInI2: (a: string) => string; propertyOnlyInI2: string; }' is not assignable to type 'I1<number> | I2<number>'.
commonPropertyType: "hello",
commonMethodType: a=> a,
commonMethodWithTypeParameter: a => a,

methodOnlyInI1: a => a,
propertyOnlyInI1: "Hello",
},
{ // Like i2
commonPropertyType: "hello",
commonMethodType: a=> a,
commonMethodWithTypeParameter: a => a,

methodOnlyInI2: a => a,
propertyOnlyInI2: "Hello",
}, { // Like i1 and i2 both
commonPropertyType: "hello",
commonMethodType: a=> a,
commonMethodWithTypeParameter: a => a,
methodOnlyInI1: a => a,
propertyOnlyInI1: "Hello",
methodOnlyInI2: a => a,
propertyOnlyInI2: "Hello",
}];

interface I11 {
commonMethodDifferentReturnType(a: string, b: number): string;
commonPropertyDifferentType: string;
}
interface I21 {
commonMethodDifferentReturnType(a: string, b: number): number;
commonPropertyDifferentType: number;
}
var i11: I11;
var i21: I21;
var i11Ori21: I11 | I21 = i11;
var i11Ori21: I11 | I21 = i21;
var i11Ori21: I11 | I21 = {
// Like i1
commonMethodDifferentReturnType: (a, b) => {
var z = a.charAt(b);
return z;
},
commonPropertyDifferentType: "hello",
};
var i11Ori21: I11 | I21 = {
// Like i2
commonMethodDifferentReturnType: (a, b) => {
var z = a.charCodeAt(b);
return z;
},
commonPropertyDifferentType: 10,
};
var arrayOrI11OrI21: Array<I11 | I21> = [i11, i21, i11 || i21, {
// Like i1
commonMethodDifferentReturnType: (a, b) => {
var z = a.charAt(b);
return z;
},
commonPropertyDifferentType: "hello",
}, {
// Like i2
commonMethodDifferentReturnType: (a, b) => {
var z = a.charCodeAt(b);
return z;
},
commonPropertyDifferentType: 10,
}];
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,6 @@ tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(
Type '{ prop: string | number; anotherP: string; }' is not assignable to type '{ prop: number; anotherP1: number; }'.
Property 'anotherP1' is missing in type '{ prop: string | number; anotherP: string; }'.
tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(29,5): error TS2322: Type '{ prop: string | number; anotherP: string; anotherP1: number; }' is not assignable to type '{ prop: string; anotherP: string; } | { prop: number; anotherP1: number; }'.
Type '{ prop: string | number; anotherP: string; anotherP1: number; }' is not assignable to type '{ prop: number; anotherP1: number; }'.
Types of property 'prop' are incompatible.
Type 'string | number' is not assignable to type 'number'.
Type 'string' is not assignable to type 'number'.
tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(57,5): error TS2322: Type '{ commonMethodDifferentReturnType: (a: string, b: number) => string | number; }' is not assignable to type 'I11 | I21'.
Type '{ commonMethodDifferentReturnType: (a: string, b: number) => string | number; }' is not assignable to type 'I21'.
Types of property 'commonMethodDifferentReturnType' are incompatible.
Expand Down Expand Up @@ -83,10 +79,6 @@ tests/cases/conformance/types/union/contextualTypeWithUnionTypeObjectLiteral.ts(
var objStrOrNum8: { prop: string; anotherP: string; } | { prop: number; anotherP1: number } = {
~~~~~~~~~~~~
!!! error TS2322: Type '{ prop: string | number; anotherP: string; anotherP1: number; }' is not assignable to type '{ prop: string; anotherP: string; } | { prop: number; anotherP1: number; }'.
!!! error TS2322: Type '{ prop: string | number; anotherP: string; anotherP1: number; }' is not assignable to type '{ prop: number; anotherP1: number; }'.
!!! error TS2322: Types of property 'prop' are incompatible.
!!! error TS2322: Type 'string | number' is not assignable to type 'number'.
!!! error TS2322: Type 'string' is not assignable to type 'number'.
prop: strOrNumber,
anotherP: str,
anotherP1: num
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
tests/cases/compiler/discriminatedUnionErrorMessage.ts(10,5): error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Shape'.
Object literal may only specify known properties, and 'x' does not exist in type 'Square'.
tests/cases/compiler/discriminatedUnionErrorMessage.ts(8,5): error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Shape'.
Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Square'.
Property 'size' is missing in type '{ kind: "sq"; x: number; y: number; }'.


==== tests/cases/compiler/discriminatedUnionErrorMessage.ts (1 errors) ====
Expand All @@ -11,11 +12,12 @@ tests/cases/compiler/discriminatedUnionErrorMessage.ts(10,5): error TS2322: Type
| Rectangle
| Circle;
let shape: Shape = {
kind: "sq",
x: 12,
~~~~~
!!! error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Shape'.
!!! error TS2322: Object literal may only specify known properties, and 'x' does not exist in type 'Square'.
!!! error TS2322: Type '{ kind: "sq"; x: number; y: number; }' is not assignable to type 'Square'.
!!! error TS2322: Property 'size' is missing in type '{ kind: "sq"; x: number; y: number; }'.
kind: "sq",
x: 12,
y: 13,
}

51 changes: 22 additions & 29 deletions tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
tests/cases/compiler/excessPropertyCheckWithUnions.ts(10,30): error TS2322: Type '{ tag: "T"; a1: string; }' is not assignable to type 'ADT'.
Object literal may only specify known properties, and 'a1' does not exist in type '{ tag: "T"; }'.
tests/cases/compiler/excessPropertyCheckWithUnions.ts(11,21): error TS2322: Type '{ tag: "A"; d20: number; }' is not assignable to type 'ADT'.
Object literal may only specify known properties, and 'd20' does not exist in type '{ tag: "A"; a1: string; }'.
tests/cases/compiler/excessPropertyCheckWithUnions.ts(11,1): error TS2322: Type '{ tag: "A"; d20: number; }' is not assignable to type 'ADT'.
Type '{ tag: "A"; d20: number; }' is not assignable to type '{ tag: "A"; a1: string; }'.
Property 'a1' is missing in type '{ tag: "A"; d20: number; }'.
tests/cases/compiler/excessPropertyCheckWithUnions.ts(12,1): error TS2322: Type '{ tag: "D"; }' is not assignable to type 'ADT'.
Type '{ tag: "D"; }' is not assignable to type '{ tag: "D"; d20: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20; }'.
Property 'd20' is missing in type '{ tag: "D"; }'.
tests/cases/compiler/excessPropertyCheckWithUnions.ts(30,1): error TS2322: Type '{ tag: "A"; x: string; y: number; }' is not assignable to type 'Ambiguous'.
tests/cases/compiler/excessPropertyCheckWithUnions.ts(33,28): error TS2322: Type '{ tag: "A"; x: string; extra: number; }' is not assignable to type 'Ambiguous'.
Object literal may only specify known properties, and 'extra' does not exist in type 'Ambiguous'.
tests/cases/compiler/excessPropertyCheckWithUnions.ts(34,26): error TS2322: Type '{ tag: "A"; y: number; extra: number; }' is not assignable to type 'Ambiguous'.
Expand All @@ -17,18 +17,14 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(40,1): error TS2322: Type
Type '{ tag: "A"; z: true; }' is not assignable to type '{ tag: "C"; }'.
Types of property 'tag' are incompatible.
Type '"A"' is not assignable to type '"C"'.
tests/cases/compiler/excessPropertyCheckWithUnions.ts(49,35): error TS2322: Type '{ a: 1; b: 1; first: string; second: string; }' is not assignable to type 'Overlapping'.
Object literal may only specify known properties, and 'second' does not exist in type '{ a: 1; b: 1; first: string; }'.
tests/cases/compiler/excessPropertyCheckWithUnions.ts(50,35): error TS2322: Type '{ a: 1; b: 1; first: string; third: string; }' is not assignable to type 'Overlapping'.
Object literal may only specify known properties, and 'third' does not exist in type '{ a: 1; b: 1; first: string; }'.
tests/cases/compiler/excessPropertyCheckWithUnions.ts(66,9): error TS2322: Type '{ kind: "A"; n: { a: string; b: string; }; }' is not assignable to type 'AB'.
Type '{ kind: "A"; n: { a: string; b: string; }; }' is not assignable to type '{ kind: "A"; n: AN; }'.
Types of property 'n' are incompatible.
Type '{ a: string; b: string; }' is not assignable to type 'AN'.
Object literal may only specify known properties, and 'b' does not exist in type 'AN'.
tests/cases/compiler/excessPropertyCheckWithUnions.ts(49,1): error TS2322: Type '{ a: 1; b: 1; first: string; second: string; }' is not assignable to type 'Overlapping'.
tests/cases/compiler/excessPropertyCheckWithUnions.ts(50,1): error TS2322: Type '{ a: 1; b: 1; first: string; third: string; }' is not assignable to type 'Overlapping'.
tests/cases/compiler/excessPropertyCheckWithUnions.ts(62,7): error TS2322: Type '{ kind: "A"; n: { a: string; b: string; }; }' is not assignable to type 'AB'.
tests/cases/compiler/excessPropertyCheckWithUnions.ts(69,7): error TS2322: Type '{ kind: "A"; n: { a: string; c: string; }; }' is not assignable to type 'AB'.
Type '{ a: string; c: string; }' is not assignable to type 'AN'.


==== tests/cases/compiler/excessPropertyCheckWithUnions.ts (10 errors) ====
==== tests/cases/compiler/excessPropertyCheckWithUnions.ts (11 errors) ====
type ADT = {
tag: "A",
a1: string
Expand All @@ -39,13 +35,11 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(66,9): error TS2322: Type
tag: "T",
}
let wrong: ADT = { tag: "T", a1: "extra" }
~~~~~~~~~~~
!!! error TS2322: Type '{ tag: "T"; a1: string; }' is not assignable to type 'ADT'.
!!! error TS2322: Object literal may only specify known properties, and 'a1' does not exist in type '{ tag: "T"; }'.
wrong = { tag: "A", d20: 12 }
~~~~~~~
~~~~~
!!! error TS2322: Type '{ tag: "A"; d20: number; }' is not assignable to type 'ADT'.
!!! error TS2322: Object literal may only specify known properties, and 'd20' does not exist in type '{ tag: "A"; a1: string; }'.
!!! error TS2322: Type '{ tag: "A"; d20: number; }' is not assignable to type '{ tag: "A"; a1: string; }'.
!!! error TS2322: Property 'a1' is missing in type '{ tag: "A"; d20: number; }'.
wrong = { tag: "D" }
~~~~~
!!! error TS2322: Type '{ tag: "D"; }' is not assignable to type 'ADT'.
Expand All @@ -69,6 +63,8 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(66,9): error TS2322: Type
amb = { tag: "A", x: "hi" }
amb = { tag: "A", y: 12 }
amb = { tag: "A", x: "hi", y: 12 }
~~~
!!! error TS2322: Type '{ tag: "A"; x: string; y: number; }' is not assignable to type 'Ambiguous'.

// correctly error on excess property 'extra', even when ambiguous
amb = { tag: "A", x: "hi", extra: 12 }
Expand Down Expand Up @@ -104,13 +100,11 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(66,9): error TS2322: Type

// these two are still errors despite their doubled up discriminants
over = { a: 1, b: 1, first: "ok", second: "error" }
~~~~~~~~~~~~~~~
~~~~
!!! error TS2322: Type '{ a: 1; b: 1; first: string; second: string; }' is not assignable to type 'Overlapping'.
!!! error TS2322: Object literal may only specify known properties, and 'second' does not exist in type '{ a: 1; b: 1; first: string; }'.
over = { a: 1, b: 1, first: "ok", third: "error" }
~~~~~~~~~~~~~~
~~~~
!!! error TS2322: Type '{ a: 1; b: 1; first: string; third: string; }' is not assignable to type 'Overlapping'.
!!! error TS2322: Object literal may only specify known properties, and 'third' does not exist in type '{ a: 1; b: 1; first: string; }'.

// Freshness disappears after spreading a union
declare let t0: { a: any, b: any } | { d: any, e: any }
Expand All @@ -123,19 +117,18 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(66,9): error TS2322: Type
type BN = { b: string }
type AB = { kind: "A", n: AN } | { kind: "B", n: BN }
const abab: AB = {
~~~~
!!! error TS2322: Type '{ kind: "A"; n: { a: string; b: string; }; }' is not assignable to type 'AB'.
kind: "A",
n: {
a: "a",
b: "b", // excess -- kind: "A"
~~~~~~
!!! error TS2322: Type '{ kind: "A"; n: { a: string; b: string; }; }' is not assignable to type 'AB'.
!!! error TS2322: Type '{ kind: "A"; n: { a: string; b: string; }; }' is not assignable to type '{ kind: "A"; n: AN; }'.
!!! error TS2322: Types of property 'n' are incompatible.
!!! error TS2322: Type '{ a: string; b: string; }' is not assignable to type 'AN'.
!!! error TS2322: Object literal may only specify known properties, and 'b' does not exist in type 'AN'.
}
}
const abac: AB = {
~~~~
!!! error TS2322: Type '{ kind: "A"; n: { a: string; c: string; }; }' is not assignable to type 'AB'.
!!! error TS2322: Type '{ a: string; c: string; }' is not assignable to type 'AN'.
kind: "A",
n: {
a: "a",
Expand Down
Loading