From 3b5caa3e0f5faec7109b5db915fe9f3bad8ecddb Mon Sep 17 00:00:00 2001 From: Matt McCutchen Date: Sun, 7 Oct 2018 02:49:37 -0400 Subject: [PATCH] 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 #27144. --- src/compiler/checker.ts | 29 ++++++++++- src/compiler/commandLineParser.ts | 4 +- src/compiler/diagnosticMessages.json | 8 ++++ ...ndexSignatureToOptionalProperty.errors.txt | 30 ++++++++++++ .../assignIndexSignatureToOptionalProperty.js | 29 +++++++++++ ...gnIndexSignatureToOptionalProperty.symbols | 48 +++++++++++++++++++ ...signIndexSignatureToOptionalProperty.types | 42 ++++++++++++++++ ...gnRestElementToOptionalProperty.errors.txt | 18 +++++++ .../assignRestElementToOptionalProperty.js | 17 +++++++ ...ssignRestElementToOptionalProperty.symbols | 16 +++++++ .../assignRestElementToOptionalProperty.types | 16 +++++++ .../assignIndexSignatureToOptionalProperty.ts | 19 ++++++++ .../assignRestElementToOptionalProperty.ts | 7 +++ 13 files changed, 280 insertions(+), 3 deletions(-) create mode 100644 tests/baselines/reference/assignIndexSignatureToOptionalProperty.errors.txt create mode 100644 tests/baselines/reference/assignIndexSignatureToOptionalProperty.js create mode 100644 tests/baselines/reference/assignIndexSignatureToOptionalProperty.symbols create mode 100644 tests/baselines/reference/assignIndexSignatureToOptionalProperty.types create mode 100644 tests/baselines/reference/assignRestElementToOptionalProperty.errors.txt create mode 100644 tests/baselines/reference/assignRestElementToOptionalProperty.js create mode 100644 tests/baselines/reference/assignRestElementToOptionalProperty.symbols create mode 100644 tests/baselines/reference/assignRestElementToOptionalProperty.types create mode 100644 tests/cases/compiler/assignIndexSignatureToOptionalProperty.ts create mode 100644 tests/cases/compiler/assignRestElementToOptionalProperty.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index ecd3312fa30cd..d001015bfea92 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -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; } @@ -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; diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 58f1e8fdaa3b1..4c883ba96f214 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -962,14 +962,14 @@ namespace ts { getOptionNameMap: () => OptionNameMap, [unknownOptionDiagnostic, optionTypeMismatchDiagnostic]: ParseCommandLineWorkerDiagnostics, commandLine: ReadonlyArray, - 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 }; diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index ce1ccf1714548..608cc8bde0c75 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -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", diff --git a/tests/baselines/reference/assignIndexSignatureToOptionalProperty.errors.txt b/tests/baselines/reference/assignIndexSignatureToOptionalProperty.errors.txt new file mode 100644 index 0000000000000..4b0987542119c --- /dev/null +++ b/tests/baselines/reference/assignIndexSignatureToOptionalProperty.errors.txt @@ -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; + \ No newline at end of file diff --git a/tests/baselines/reference/assignIndexSignatureToOptionalProperty.js b/tests/baselines/reference/assignIndexSignatureToOptionalProperty.js new file mode 100644 index 0000000000000..5289dc8a61275 --- /dev/null +++ b/tests/baselines/reference/assignIndexSignatureToOptionalProperty.js @@ -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; diff --git a/tests/baselines/reference/assignIndexSignatureToOptionalProperty.symbols b/tests/baselines/reference/assignIndexSignatureToOptionalProperty.symbols new file mode 100644 index 0000000000000..77c9e0e41e3f5 --- /dev/null +++ b/tests/baselines/reference/assignIndexSignatureToOptionalProperty.symbols @@ -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)) + diff --git a/tests/baselines/reference/assignIndexSignatureToOptionalProperty.types b/tests/baselines/reference/assignIndexSignatureToOptionalProperty.types new file mode 100644 index 0000000000000..fce5e4952e201 --- /dev/null +++ b/tests/baselines/reference/assignIndexSignatureToOptionalProperty.types @@ -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 + diff --git a/tests/baselines/reference/assignRestElementToOptionalProperty.errors.txt b/tests/baselines/reference/assignRestElementToOptionalProperty.errors.txt new file mode 100644 index 0000000000000..7d7b81652c9a4 --- /dev/null +++ b/tests/baselines/reference/assignRestElementToOptionalProperty.errors.txt @@ -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; + \ No newline at end of file diff --git a/tests/baselines/reference/assignRestElementToOptionalProperty.js b/tests/baselines/reference/assignRestElementToOptionalProperty.js new file mode 100644 index 0000000000000..8500202a458e6 --- /dev/null +++ b/tests/baselines/reference/assignRestElementToOptionalProperty.js @@ -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; diff --git a/tests/baselines/reference/assignRestElementToOptionalProperty.symbols b/tests/baselines/reference/assignRestElementToOptionalProperty.symbols new file mode 100644 index 0000000000000..dbfc353922ce8 --- /dev/null +++ b/tests/baselines/reference/assignRestElementToOptionalProperty.symbols @@ -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)) + diff --git a/tests/baselines/reference/assignRestElementToOptionalProperty.types b/tests/baselines/reference/assignRestElementToOptionalProperty.types new file mode 100644 index 0000000000000..84033e50cb18b --- /dev/null +++ b/tests/baselines/reference/assignRestElementToOptionalProperty.types @@ -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[]] + diff --git a/tests/cases/compiler/assignIndexSignatureToOptionalProperty.ts b/tests/cases/compiler/assignIndexSignatureToOptionalProperty.ts new file mode 100644 index 0000000000000..26832ad9a5762 --- /dev/null +++ b/tests/cases/compiler/assignIndexSignatureToOptionalProperty.ts @@ -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; diff --git a/tests/cases/compiler/assignRestElementToOptionalProperty.ts b/tests/cases/compiler/assignRestElementToOptionalProperty.ts new file mode 100644 index 0000000000000..0490db5088e06 --- /dev/null +++ b/tests/cases/compiler/assignRestElementToOptionalProperty.ts @@ -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;