diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 645bf049f4e12..85273ece816c2 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -16494,13 +16494,40 @@ namespace ts { const name = targetProp.escapedName; if (!(targetProp.flags & SymbolFlags.Prototype) && (!numericNamesOnly || isNumericLiteralName(name) || name === "length")) { const sourceProp = getPropertyOfType(source, name); - if (sourceProp && sourceProp !== targetProp) { + if (sourceProp === targetProp) { + continue; + } + if (sourceProp) { const related = propertyRelatedTo(source, target, sourceProp, targetProp, getTypeOfSymbol, reportErrors, intersectionState); if (!related) { return Ternary.False; } result &= related; } + else if (!targetProp.valueDeclaration || !isPrivateIdentifierPropertyDeclaration(targetProp.valueDeclaration)) { + 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 27f502a3f7de1..850825f5f230c 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -1196,7 +1196,7 @@ namespace ts { export function parseCommandLineWorker( diagnostics: ParseCommandLineWorkerDiagnostics, commandLine: readonly string[], - readFile?: (path: string) => string | undefined) { + readFile?: (path: string) => string | undefined): ParsedCommandLine { const options = {} as OptionsBase; let watchOptions: WatchOptions | undefined; const fileNames: string[] = []; @@ -1204,7 +1204,7 @@ namespace ts { parseStrings(commandLine); return { - options, + options: options as CompilerOptions, watchOptions, fileNames, errors diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index c8bd3ceb7b79b..656a61c263765 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2907,6 +2907,14 @@ "category": "Error", "code": 2784 }, + "Index signature is incompatible with property '{0}'.": { + "category": "Error", + "code": 2785 + }, + "Rest element type is incompatible with property '{0}'.": { + "category": "Error", + "code": 2786 + }, "Import declaration '{0}' is using private name '{1}'.": { "category": "Error", diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index a79070dde80f7..3249bac6a8896 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -2641,7 +2641,7 @@ namespace ts.server { } if (args.watchOptions) { - this.hostConfiguration.watchOptions = convertWatchOptions(args.watchOptions); + this.hostConfiguration.watchOptions = convertWatchOptions(args.watchOptions as any); this.logger.info(`Host watch options changed to ${JSON.stringify(this.hostConfiguration.watchOptions)}, it will be take effect for next watches.`); } } diff --git a/tests/baselines/reference/APISample_WatchWithOwnWatchHost.js b/tests/baselines/reference/APISample_WatchWithOwnWatchHost.js index 75444a614f952..b9a7187eb0726 100644 --- a/tests/baselines/reference/APISample_WatchWithOwnWatchHost.js +++ b/tests/baselines/reference/APISample_WatchWithOwnWatchHost.js @@ -36,8 +36,8 @@ function watchMain() { readDirectory: ts.sys.readDirectory, realpath: ts.sys.realpath, - watchFile: ts.sys.watchFile!, - watchDirectory: ts.sys.watchDirectory!, + watchFile: ts.sys.watchFile! as any, + watchDirectory: ts.sys.watchDirectory! as any, createProgram: ts.createAbstractBuilder }; 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/APISample_WatchWithOwnWatchHost.ts b/tests/cases/compiler/APISample_WatchWithOwnWatchHost.ts index 18cb5766ce331..c7f9963a1f8d7 100644 --- a/tests/cases/compiler/APISample_WatchWithOwnWatchHost.ts +++ b/tests/cases/compiler/APISample_WatchWithOwnWatchHost.ts @@ -40,8 +40,8 @@ function watchMain() { readDirectory: ts.sys.readDirectory, realpath: ts.sys.realpath, - watchFile: ts.sys.watchFile!, - watchDirectory: ts.sys.watchDirectory!, + watchFile: ts.sys.watchFile! as any, + watchDirectory: ts.sys.watchDirectory! as any, createProgram: ts.createAbstractBuilder }; 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;