Skip to content

Commit

Permalink
Assignability should check an index signature or rest type of the source
Browse files Browse the repository at this point in the history
against optional properties of the target.

Fix one good break in the compiler itself.

Fixes microsoft#27144.
  • Loading branch information
mattmccutchen committed Oct 7, 2018
1 parent 85a3475 commit 3b5caa3
Show file tree
Hide file tree
Showing 13 changed files with 280 additions and 3 deletions.
29 changes: 28 additions & 1 deletion src/compiler/checker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12148,7 +12148,10 @@ namespace ts {
for (const targetProp of properties) {
if (!(targetProp.flags & SymbolFlags.Prototype)) {
const sourceProp = getPropertyOfType(source, targetProp.escapedName);
if (sourceProp && sourceProp !== targetProp) {
if (sourceProp === targetProp) {
// OK
}
else if (sourceProp) {
if (isIgnoredJsxProperty(source, sourceProp, getTypeOfSymbol(targetProp))) {
continue;
}
Expand Down Expand Up @@ -12216,6 +12219,30 @@ namespace ts {
return Ternary.False;
}
}
else {
Debug.assert(!!(targetProp.flags & SymbolFlags.Optional));
let sourcePropType, diagnostic;
if (isTupleType(source)) {
sourcePropType = isNumericLiteralName(targetProp.escapedName) && +targetProp.escapedName >= 0 ? getRestTypeOfTupleType(source) : undefined;
diagnostic = Diagnostics.Rest_element_type_is_incompatible_with_property_0;
}
else {
sourcePropType =
isNumericLiteralName(targetProp.escapedName) && getIndexTypeOfType(source, IndexKind.Number) ||
getIndexTypeOfType(source, IndexKind.String);
diagnostic = Diagnostics.Index_signature_is_incompatible_with_property_0;
}
if (sourcePropType) {
const related = isRelatedTo(sourcePropType, getTypeOfSymbol(targetProp), reportErrors);
if (!related) {
if (reportErrors) {
reportError(diagnostic, symbolToString(targetProp));
}
return Ternary.False;
}
result &= related;
}
}
}
}
return result;
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/commandLineParser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -962,14 +962,14 @@ namespace ts {
getOptionNameMap: () => OptionNameMap,
[unknownOptionDiagnostic, optionTypeMismatchDiagnostic]: ParseCommandLineWorkerDiagnostics,
commandLine: ReadonlyArray<string>,
readFile?: (path: string) => string | undefined) {
readFile?: (path: string) => string | undefined): ParsedCommandLine {
const options = {} as OptionsBase;
const fileNames: string[] = [];
const errors: Diagnostic[] = [];

parseStrings(commandLine);
return {
options,
options: options as CompilerOptions,
fileNames,
errors
};
Expand Down
8 changes: 8 additions & 0 deletions src/compiler/diagnosticMessages.json
Original file line number Diff line number Diff line change
Expand Up @@ -2489,6 +2489,14 @@
"category": "Error",
"code": 2734
},
"Index signature is incompatible with property '{0}'.": {
"category": "Error",
"code": 2735
},
"Rest element type is incompatible with property '{0}'.": {
"category": "Error",
"code": 2736
},

"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
tests/cases/compiler/assignIndexSignatureToOptionalProperty.ts(17,5): error TS2322: Type 'Bar' is not assignable to type 'Foo'.
Index signature is incompatible with property 'b'.
Type 'number' is not assignable to type 'string'.


==== tests/cases/compiler/assignIndexSignatureToOptionalProperty.ts (1 errors) ====
// #27144

interface Foo {
a: number;
b?: string;
}
interface Foo2 {
a: number;
b?: number;
}
interface Bar {
a: number;
[n: string]: number;
}
let b: Bar = { a: 42, b: 43 };
// Error, index signature does not match optional property `b`
let f: Foo = b;
~
!!! error TS2322: Type 'Bar' is not assignable to type 'Foo'.
!!! error TS2322: Index signature is incompatible with property 'b'.
!!! error TS2322: Type 'number' is not assignable to type 'string'.
// OK
let f2: Foo2 = b;

Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//// [assignIndexSignatureToOptionalProperty.ts]
// #27144

interface Foo {
a: number;
b?: string;
}
interface Foo2 {
a: number;
b?: number;
}
interface Bar {
a: number;
[n: string]: number;
}
let b: Bar = { a: 42, b: 43 };
// Error, index signature does not match optional property `b`
let f: Foo = b;
// OK
let f2: Foo2 = b;


//// [assignIndexSignatureToOptionalProperty.js]
// #27144
var b = { a: 42, b: 43 };
// Error, index signature does not match optional property `b`
var f = b;
// OK
var f2 = b;
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
=== tests/cases/compiler/assignIndexSignatureToOptionalProperty.ts ===
// #27144

interface Foo {
>Foo : Symbol(Foo, Decl(assignIndexSignatureToOptionalProperty.ts, 0, 0))

a: number;
>a : Symbol(Foo.a, Decl(assignIndexSignatureToOptionalProperty.ts, 2, 15))

b?: string;
>b : Symbol(Foo.b, Decl(assignIndexSignatureToOptionalProperty.ts, 3, 14))
}
interface Foo2 {
>Foo2 : Symbol(Foo2, Decl(assignIndexSignatureToOptionalProperty.ts, 5, 1))

a: number;
>a : Symbol(Foo2.a, Decl(assignIndexSignatureToOptionalProperty.ts, 6, 16))

b?: number;
>b : Symbol(Foo2.b, Decl(assignIndexSignatureToOptionalProperty.ts, 7, 14))
}
interface Bar {
>Bar : Symbol(Bar, Decl(assignIndexSignatureToOptionalProperty.ts, 9, 1))

a: number;
>a : Symbol(Bar.a, Decl(assignIndexSignatureToOptionalProperty.ts, 10, 15))

[n: string]: number;
>n : Symbol(n, Decl(assignIndexSignatureToOptionalProperty.ts, 12, 5))
}
let b: Bar = { a: 42, b: 43 };
>b : Symbol(b, Decl(assignIndexSignatureToOptionalProperty.ts, 14, 3))
>Bar : Symbol(Bar, Decl(assignIndexSignatureToOptionalProperty.ts, 9, 1))
>a : Symbol(a, Decl(assignIndexSignatureToOptionalProperty.ts, 14, 14))
>b : Symbol(b, Decl(assignIndexSignatureToOptionalProperty.ts, 14, 21))

// Error, index signature does not match optional property `b`
let f: Foo = b;
>f : Symbol(f, Decl(assignIndexSignatureToOptionalProperty.ts, 16, 3))
>Foo : Symbol(Foo, Decl(assignIndexSignatureToOptionalProperty.ts, 0, 0))
>b : Symbol(b, Decl(assignIndexSignatureToOptionalProperty.ts, 14, 3))

// OK
let f2: Foo2 = b;
>f2 : Symbol(f2, Decl(assignIndexSignatureToOptionalProperty.ts, 18, 3))
>Foo2 : Symbol(Foo2, Decl(assignIndexSignatureToOptionalProperty.ts, 5, 1))
>b : Symbol(b, Decl(assignIndexSignatureToOptionalProperty.ts, 14, 3))

Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
=== tests/cases/compiler/assignIndexSignatureToOptionalProperty.ts ===
// #27144

interface Foo {
a: number;
>a : number

b?: string;
>b : string
}
interface Foo2 {
a: number;
>a : number

b?: number;
>b : number
}
interface Bar {
a: number;
>a : number

[n: string]: number;
>n : string
}
let b: Bar = { a: 42, b: 43 };
>b : Bar
>{ a: 42, b: 43 } : { a: number; b: number; }
>a : number
>42 : 42
>b : number
>43 : 43

// Error, index signature does not match optional property `b`
let f: Foo = b;
>f : Foo
>b : Bar

// OK
let f2: Foo2 = b;
>f2 : Foo2
>b : Bar

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
tests/cases/compiler/assignRestElementToOptionalProperty.ts(5,5): error TS2322: Type '[number, ...string[]]' is not assignable to type '[number, number?, ...string[]]'.
Rest element type is incompatible with property '1'.
Type 'string' is not assignable to type 'number'.


==== tests/cases/compiler/assignRestElementToOptionalProperty.ts (1 errors) ====
// Inspired by #27144

let t: [number, ...string[]];
// Error, rest type of `t` does not match element 1 of `t2`
let t2: [number, number?, ...string[]] = t;
~~
!!! error TS2322: Type '[number, ...string[]]' is not assignable to type '[number, number?, ...string[]]'.
!!! error TS2322: Rest element type is incompatible with property '1'.
!!! error TS2322: Type 'string' is not assignable to type 'number'.
// OK
let t3: [number, string?, ...string[]] = t;

17 changes: 17 additions & 0 deletions tests/baselines/reference/assignRestElementToOptionalProperty.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
//// [assignRestElementToOptionalProperty.ts]
// Inspired by #27144

let t: [number, ...string[]];
// Error, rest type of `t` does not match element 1 of `t2`
let t2: [number, number?, ...string[]] = t;
// OK
let t3: [number, string?, ...string[]] = t;


//// [assignRestElementToOptionalProperty.js]
// Inspired by #27144
var t;
// Error, rest type of `t` does not match element 1 of `t2`
var t2 = t;
// OK
var t3 = t;
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
=== tests/cases/compiler/assignRestElementToOptionalProperty.ts ===
// Inspired by #27144

let t: [number, ...string[]];
>t : Symbol(t, Decl(assignRestElementToOptionalProperty.ts, 2, 3))

// Error, rest type of `t` does not match element 1 of `t2`
let t2: [number, number?, ...string[]] = t;
>t2 : Symbol(t2, Decl(assignRestElementToOptionalProperty.ts, 4, 3))
>t : Symbol(t, Decl(assignRestElementToOptionalProperty.ts, 2, 3))

// OK
let t3: [number, string?, ...string[]] = t;
>t3 : Symbol(t3, Decl(assignRestElementToOptionalProperty.ts, 6, 3))
>t : Symbol(t, Decl(assignRestElementToOptionalProperty.ts, 2, 3))

Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
=== tests/cases/compiler/assignRestElementToOptionalProperty.ts ===
// Inspired by #27144

let t: [number, ...string[]];
>t : [number, ...string[]]

// Error, rest type of `t` does not match element 1 of `t2`
let t2: [number, number?, ...string[]] = t;
>t2 : [number, number?, ...string[]]
>t : [number, ...string[]]

// OK
let t3: [number, string?, ...string[]] = t;
>t3 : [number, string?, ...string[]]
>t : [number, ...string[]]

19 changes: 19 additions & 0 deletions tests/cases/compiler/assignIndexSignatureToOptionalProperty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// #27144

interface Foo {
a: number;
b?: string;
}
interface Foo2 {
a: number;
b?: number;
}
interface Bar {
a: number;
[n: string]: number;
}
let b: Bar = { a: 42, b: 43 };
// Error, index signature does not match optional property `b`
let f: Foo = b;
// OK
let f2: Foo2 = b;
7 changes: 7 additions & 0 deletions tests/cases/compiler/assignRestElementToOptionalProperty.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
// Inspired by #27144

let t: [number, ...string[]];
// Error, rest type of `t` does not match element 1 of `t2`
let t2: [number, number?, ...string[]] = t;
// OK
let t3: [number, string?, ...string[]] = t;

0 comments on commit 3b5caa3

Please sign in to comment.