Skip to content

Commit 3b5caa3

Browse files
committed
Assignability should check an index signature or rest type of the source
against optional properties of the target. Fix one good break in the compiler itself. Fixes microsoft#27144.
1 parent 85a3475 commit 3b5caa3

13 files changed

+280
-3
lines changed

src/compiler/checker.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12148,7 +12148,10 @@ namespace ts {
1214812148
for (const targetProp of properties) {
1214912149
if (!(targetProp.flags & SymbolFlags.Prototype)) {
1215012150
const sourceProp = getPropertyOfType(source, targetProp.escapedName);
12151-
if (sourceProp && sourceProp !== targetProp) {
12151+
if (sourceProp === targetProp) {
12152+
// OK
12153+
}
12154+
else if (sourceProp) {
1215212155
if (isIgnoredJsxProperty(source, sourceProp, getTypeOfSymbol(targetProp))) {
1215312156
continue;
1215412157
}
@@ -12216,6 +12219,30 @@ namespace ts {
1221612219
return Ternary.False;
1221712220
}
1221812221
}
12222+
else {
12223+
Debug.assert(!!(targetProp.flags & SymbolFlags.Optional));
12224+
let sourcePropType, diagnostic;
12225+
if (isTupleType(source)) {
12226+
sourcePropType = isNumericLiteralName(targetProp.escapedName) && +targetProp.escapedName >= 0 ? getRestTypeOfTupleType(source) : undefined;
12227+
diagnostic = Diagnostics.Rest_element_type_is_incompatible_with_property_0;
12228+
}
12229+
else {
12230+
sourcePropType =
12231+
isNumericLiteralName(targetProp.escapedName) && getIndexTypeOfType(source, IndexKind.Number) ||
12232+
getIndexTypeOfType(source, IndexKind.String);
12233+
diagnostic = Diagnostics.Index_signature_is_incompatible_with_property_0;
12234+
}
12235+
if (sourcePropType) {
12236+
const related = isRelatedTo(sourcePropType, getTypeOfSymbol(targetProp), reportErrors);
12237+
if (!related) {
12238+
if (reportErrors) {
12239+
reportError(diagnostic, symbolToString(targetProp));
12240+
}
12241+
return Ternary.False;
12242+
}
12243+
result &= related;
12244+
}
12245+
}
1221912246
}
1222012247
}
1222112248
return result;

src/compiler/commandLineParser.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -962,14 +962,14 @@ namespace ts {
962962
getOptionNameMap: () => OptionNameMap,
963963
[unknownOptionDiagnostic, optionTypeMismatchDiagnostic]: ParseCommandLineWorkerDiagnostics,
964964
commandLine: ReadonlyArray<string>,
965-
readFile?: (path: string) => string | undefined) {
965+
readFile?: (path: string) => string | undefined): ParsedCommandLine {
966966
const options = {} as OptionsBase;
967967
const fileNames: string[] = [];
968968
const errors: Diagnostic[] = [];
969969

970970
parseStrings(commandLine);
971971
return {
972-
options,
972+
options: options as CompilerOptions,
973973
fileNames,
974974
errors
975975
};

src/compiler/diagnosticMessages.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2489,6 +2489,14 @@
24892489
"category": "Error",
24902490
"code": 2734
24912491
},
2492+
"Index signature is incompatible with property '{0}'.": {
2493+
"category": "Error",
2494+
"code": 2735
2495+
},
2496+
"Rest element type is incompatible with property '{0}'.": {
2497+
"category": "Error",
2498+
"code": 2736
2499+
},
24922500

24932501
"Import declaration '{0}' is using private name '{1}'.": {
24942502
"category": "Error",
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
tests/cases/compiler/assignIndexSignatureToOptionalProperty.ts(17,5): error TS2322: Type 'Bar' is not assignable to type 'Foo'.
2+
Index signature is incompatible with property 'b'.
3+
Type 'number' is not assignable to type 'string'.
4+
5+
6+
==== tests/cases/compiler/assignIndexSignatureToOptionalProperty.ts (1 errors) ====
7+
// #27144
8+
9+
interface Foo {
10+
a: number;
11+
b?: string;
12+
}
13+
interface Foo2 {
14+
a: number;
15+
b?: number;
16+
}
17+
interface Bar {
18+
a: number;
19+
[n: string]: number;
20+
}
21+
let b: Bar = { a: 42, b: 43 };
22+
// Error, index signature does not match optional property `b`
23+
let f: Foo = b;
24+
~
25+
!!! error TS2322: Type 'Bar' is not assignable to type 'Foo'.
26+
!!! error TS2322: Index signature is incompatible with property 'b'.
27+
!!! error TS2322: Type 'number' is not assignable to type 'string'.
28+
// OK
29+
let f2: Foo2 = b;
30+
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//// [assignIndexSignatureToOptionalProperty.ts]
2+
// #27144
3+
4+
interface Foo {
5+
a: number;
6+
b?: string;
7+
}
8+
interface Foo2 {
9+
a: number;
10+
b?: number;
11+
}
12+
interface Bar {
13+
a: number;
14+
[n: string]: number;
15+
}
16+
let b: Bar = { a: 42, b: 43 };
17+
// Error, index signature does not match optional property `b`
18+
let f: Foo = b;
19+
// OK
20+
let f2: Foo2 = b;
21+
22+
23+
//// [assignIndexSignatureToOptionalProperty.js]
24+
// #27144
25+
var b = { a: 42, b: 43 };
26+
// Error, index signature does not match optional property `b`
27+
var f = b;
28+
// OK
29+
var f2 = b;
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
=== tests/cases/compiler/assignIndexSignatureToOptionalProperty.ts ===
2+
// #27144
3+
4+
interface Foo {
5+
>Foo : Symbol(Foo, Decl(assignIndexSignatureToOptionalProperty.ts, 0, 0))
6+
7+
a: number;
8+
>a : Symbol(Foo.a, Decl(assignIndexSignatureToOptionalProperty.ts, 2, 15))
9+
10+
b?: string;
11+
>b : Symbol(Foo.b, Decl(assignIndexSignatureToOptionalProperty.ts, 3, 14))
12+
}
13+
interface Foo2 {
14+
>Foo2 : Symbol(Foo2, Decl(assignIndexSignatureToOptionalProperty.ts, 5, 1))
15+
16+
a: number;
17+
>a : Symbol(Foo2.a, Decl(assignIndexSignatureToOptionalProperty.ts, 6, 16))
18+
19+
b?: number;
20+
>b : Symbol(Foo2.b, Decl(assignIndexSignatureToOptionalProperty.ts, 7, 14))
21+
}
22+
interface Bar {
23+
>Bar : Symbol(Bar, Decl(assignIndexSignatureToOptionalProperty.ts, 9, 1))
24+
25+
a: number;
26+
>a : Symbol(Bar.a, Decl(assignIndexSignatureToOptionalProperty.ts, 10, 15))
27+
28+
[n: string]: number;
29+
>n : Symbol(n, Decl(assignIndexSignatureToOptionalProperty.ts, 12, 5))
30+
}
31+
let b: Bar = { a: 42, b: 43 };
32+
>b : Symbol(b, Decl(assignIndexSignatureToOptionalProperty.ts, 14, 3))
33+
>Bar : Symbol(Bar, Decl(assignIndexSignatureToOptionalProperty.ts, 9, 1))
34+
>a : Symbol(a, Decl(assignIndexSignatureToOptionalProperty.ts, 14, 14))
35+
>b : Symbol(b, Decl(assignIndexSignatureToOptionalProperty.ts, 14, 21))
36+
37+
// Error, index signature does not match optional property `b`
38+
let f: Foo = b;
39+
>f : Symbol(f, Decl(assignIndexSignatureToOptionalProperty.ts, 16, 3))
40+
>Foo : Symbol(Foo, Decl(assignIndexSignatureToOptionalProperty.ts, 0, 0))
41+
>b : Symbol(b, Decl(assignIndexSignatureToOptionalProperty.ts, 14, 3))
42+
43+
// OK
44+
let f2: Foo2 = b;
45+
>f2 : Symbol(f2, Decl(assignIndexSignatureToOptionalProperty.ts, 18, 3))
46+
>Foo2 : Symbol(Foo2, Decl(assignIndexSignatureToOptionalProperty.ts, 5, 1))
47+
>b : Symbol(b, Decl(assignIndexSignatureToOptionalProperty.ts, 14, 3))
48+
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
=== tests/cases/compiler/assignIndexSignatureToOptionalProperty.ts ===
2+
// #27144
3+
4+
interface Foo {
5+
a: number;
6+
>a : number
7+
8+
b?: string;
9+
>b : string
10+
}
11+
interface Foo2 {
12+
a: number;
13+
>a : number
14+
15+
b?: number;
16+
>b : number
17+
}
18+
interface Bar {
19+
a: number;
20+
>a : number
21+
22+
[n: string]: number;
23+
>n : string
24+
}
25+
let b: Bar = { a: 42, b: 43 };
26+
>b : Bar
27+
>{ a: 42, b: 43 } : { a: number; b: number; }
28+
>a : number
29+
>42 : 42
30+
>b : number
31+
>43 : 43
32+
33+
// Error, index signature does not match optional property `b`
34+
let f: Foo = b;
35+
>f : Foo
36+
>b : Bar
37+
38+
// OK
39+
let f2: Foo2 = b;
40+
>f2 : Foo2
41+
>b : Bar
42+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
tests/cases/compiler/assignRestElementToOptionalProperty.ts(5,5): error TS2322: Type '[number, ...string[]]' is not assignable to type '[number, number?, ...string[]]'.
2+
Rest element type is incompatible with property '1'.
3+
Type 'string' is not assignable to type 'number'.
4+
5+
6+
==== tests/cases/compiler/assignRestElementToOptionalProperty.ts (1 errors) ====
7+
// Inspired by #27144
8+
9+
let t: [number, ...string[]];
10+
// Error, rest type of `t` does not match element 1 of `t2`
11+
let t2: [number, number?, ...string[]] = t;
12+
~~
13+
!!! error TS2322: Type '[number, ...string[]]' is not assignable to type '[number, number?, ...string[]]'.
14+
!!! error TS2322: Rest element type is incompatible with property '1'.
15+
!!! error TS2322: Type 'string' is not assignable to type 'number'.
16+
// OK
17+
let t3: [number, string?, ...string[]] = t;
18+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//// [assignRestElementToOptionalProperty.ts]
2+
// Inspired by #27144
3+
4+
let t: [number, ...string[]];
5+
// Error, rest type of `t` does not match element 1 of `t2`
6+
let t2: [number, number?, ...string[]] = t;
7+
// OK
8+
let t3: [number, string?, ...string[]] = t;
9+
10+
11+
//// [assignRestElementToOptionalProperty.js]
12+
// Inspired by #27144
13+
var t;
14+
// Error, rest type of `t` does not match element 1 of `t2`
15+
var t2 = t;
16+
// OK
17+
var t3 = t;
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
=== tests/cases/compiler/assignRestElementToOptionalProperty.ts ===
2+
// Inspired by #27144
3+
4+
let t: [number, ...string[]];
5+
>t : Symbol(t, Decl(assignRestElementToOptionalProperty.ts, 2, 3))
6+
7+
// Error, rest type of `t` does not match element 1 of `t2`
8+
let t2: [number, number?, ...string[]] = t;
9+
>t2 : Symbol(t2, Decl(assignRestElementToOptionalProperty.ts, 4, 3))
10+
>t : Symbol(t, Decl(assignRestElementToOptionalProperty.ts, 2, 3))
11+
12+
// OK
13+
let t3: [number, string?, ...string[]] = t;
14+
>t3 : Symbol(t3, Decl(assignRestElementToOptionalProperty.ts, 6, 3))
15+
>t : Symbol(t, Decl(assignRestElementToOptionalProperty.ts, 2, 3))
16+
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
=== tests/cases/compiler/assignRestElementToOptionalProperty.ts ===
2+
// Inspired by #27144
3+
4+
let t: [number, ...string[]];
5+
>t : [number, ...string[]]
6+
7+
// Error, rest type of `t` does not match element 1 of `t2`
8+
let t2: [number, number?, ...string[]] = t;
9+
>t2 : [number, number?, ...string[]]
10+
>t : [number, ...string[]]
11+
12+
// OK
13+
let t3: [number, string?, ...string[]] = t;
14+
>t3 : [number, string?, ...string[]]
15+
>t : [number, ...string[]]
16+
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
// #27144
2+
3+
interface Foo {
4+
a: number;
5+
b?: string;
6+
}
7+
interface Foo2 {
8+
a: number;
9+
b?: number;
10+
}
11+
interface Bar {
12+
a: number;
13+
[n: string]: number;
14+
}
15+
let b: Bar = { a: 42, b: 43 };
16+
// Error, index signature does not match optional property `b`
17+
let f: Foo = b;
18+
// OK
19+
let f2: Foo2 = b;
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Inspired by #27144
2+
3+
let t: [number, ...string[]];
4+
// Error, rest type of `t` does not match element 1 of `t2`
5+
let t2: [number, number?, ...string[]] = t;
6+
// OK
7+
let t3: [number, string?, ...string[]] = t;

0 commit comments

Comments
 (0)