From cc1c2ab6b2c2165b393b8aa3397e3a7a11b8f33b Mon Sep 17 00:00:00 2001 From: Matt McCutchen Date: Fri, 17 Aug 2018 21:35:03 -0400 Subject: [PATCH 1/6] Go back to the old narrowing algorithm (pre #26143) and avoid #26130 by skipping narrowing if the old algorithm produces a type to which the assigned type is not assignable. This also means we'll no longer narrow for erroneous assignments where the assigned type is not assignable to the declared type. This is the reason for the numericLiteralTypes3 baseline change. Fixes #26405. --- src/compiler/checker.ts | 20 +++++++++++++++-- .../reference/assignmentTypeNarrowing.js | 8 +++++++ .../reference/assignmentTypeNarrowing.symbols | 20 +++++++++++++++++ .../reference/assignmentTypeNarrowing.types | 22 +++++++++++++++++++ .../reference/enumAssignmentCompat3.types | 4 ++-- .../reference/numericLiteralTypes3.types | 4 ++-- .../assignmentTypeNarrowing.ts | 6 +++++ 7 files changed, 78 insertions(+), 6 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 26b93b19335..0b357bd771d 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -13900,6 +13900,18 @@ namespace ts { return flow.id; } + function typeMaybeAssignableTo(source: Type, target: Type) { + if (!(source.flags & TypeFlags.Union)) { + return isTypeAssignableTo(source, target); + } + for (const t of (source).types) { + if (isTypeAssignableTo(t, target)) { + return true; + } + } + return false; + } + // Remove those constituent types of declaredType to which no constituent type of assignedType is assignable. // For example, when a variable of type number | string | boolean is assigned a value of type number | boolean, // we remove type string. @@ -13908,8 +13920,12 @@ namespace ts { if (assignedType.flags & TypeFlags.Never) { return assignedType; } - const reducedType = filterType(declaredType, t => isTypeComparableTo(assignedType, t)); - if (!(reducedType.flags & TypeFlags.Never)) { + const reducedType = filterType(declaredType, t => typeMaybeAssignableTo(assignedType, t)); + // Our crude heuristic produces an invalid result in some cases: see GH#26130. + // For now, when that happens, we give up and don't narrow at all. (This also + // means we'll never narrow for erroneous assignments where the assigned type + // is not assignable to the declared type.) + if (isTypeAssignableTo(assignedType, reducedType)) { return reducedType; } } diff --git a/tests/baselines/reference/assignmentTypeNarrowing.js b/tests/baselines/reference/assignmentTypeNarrowing.js index 4d6b911b88b..24a08539eb7 100644 --- a/tests/baselines/reference/assignmentTypeNarrowing.js +++ b/tests/baselines/reference/assignmentTypeNarrowing.js @@ -27,6 +27,12 @@ let a: string[]; for (x of a) { x; // string } + +// Repro from #26405 + +type AOrArrA = T | T[]; +const arr: AOrArrA<{x?: "ok"}> = [{ x: "ok" }]; // weak type +arr.push({ x: "ok" }); //// [assignmentTypeNarrowing.js] @@ -51,3 +57,5 @@ for (var _i = 0, a_1 = a; _i < a_1.length; _i++) { x = a_1[_i]; x; // string } +var arr = [{ x: "ok" }]; // weak type +arr.push({ x: "ok" }); diff --git a/tests/baselines/reference/assignmentTypeNarrowing.symbols b/tests/baselines/reference/assignmentTypeNarrowing.symbols index b9e8f3f136d..36a2cf44dae 100644 --- a/tests/baselines/reference/assignmentTypeNarrowing.symbols +++ b/tests/baselines/reference/assignmentTypeNarrowing.symbols @@ -62,3 +62,23 @@ for (x of a) { >x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 0, 3)) } +// Repro from #26405 + +type AOrArrA = T | T[]; +>AOrArrA : Symbol(AOrArrA, Decl(assignmentTypeNarrowing.ts, 27, 1)) +>T : Symbol(T, Decl(assignmentTypeNarrowing.ts, 31, 13)) +>T : Symbol(T, Decl(assignmentTypeNarrowing.ts, 31, 13)) +>T : Symbol(T, Decl(assignmentTypeNarrowing.ts, 31, 13)) + +const arr: AOrArrA<{x?: "ok"}> = [{ x: "ok" }]; // weak type +>arr : Symbol(arr, Decl(assignmentTypeNarrowing.ts, 32, 5)) +>AOrArrA : Symbol(AOrArrA, Decl(assignmentTypeNarrowing.ts, 27, 1)) +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 32, 20)) +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 32, 35)) + +arr.push({ x: "ok" }); +>arr.push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>arr : Symbol(arr, Decl(assignmentTypeNarrowing.ts, 32, 5)) +>push : Symbol(Array.push, Decl(lib.es5.d.ts, --, --)) +>x : Symbol(x, Decl(assignmentTypeNarrowing.ts, 33, 10)) + diff --git a/tests/baselines/reference/assignmentTypeNarrowing.types b/tests/baselines/reference/assignmentTypeNarrowing.types index 2e2e6ad44eb..15b71c259ae 100644 --- a/tests/baselines/reference/assignmentTypeNarrowing.types +++ b/tests/baselines/reference/assignmentTypeNarrowing.types @@ -96,3 +96,25 @@ for (x of a) { >x : string } +// Repro from #26405 + +type AOrArrA = T | T[]; +>AOrArrA : AOrArrA + +const arr: AOrArrA<{x?: "ok"}> = [{ x: "ok" }]; // weak type +>arr : AOrArrA<{ x?: "ok"; }> +>x : "ok" +>[{ x: "ok" }] : { x: "ok"; }[] +>{ x: "ok" } : { x: "ok"; } +>x : "ok" +>"ok" : "ok" + +arr.push({ x: "ok" }); +>arr.push({ x: "ok" }) : number +>arr.push : (...items: { x?: "ok"; }[]) => number +>arr : { x?: "ok"; }[] +>push : (...items: { x?: "ok"; }[]) => number +>{ x: "ok" } : { x: "ok"; } +>x : "ok" +>"ok" : "ok" + diff --git a/tests/baselines/reference/enumAssignmentCompat3.types b/tests/baselines/reference/enumAssignmentCompat3.types index f2a693515e5..a152cd624df 100644 --- a/tests/baselines/reference/enumAssignmentCompat3.types +++ b/tests/baselines/reference/enumAssignmentCompat3.types @@ -252,9 +252,9 @@ abc = merged; // missing 'd' >merged : Merged.E merged = abc; // ok ->merged = abc : First.E.a | First.E.b +>merged = abc : First.E >merged : Merged.E ->abc : First.E.a | First.E.b +>abc : First.E abc = merged2; // ok >abc = merged2 : Merged2.E diff --git a/tests/baselines/reference/numericLiteralTypes3.types b/tests/baselines/reference/numericLiteralTypes3.types index 3fede24475c..9748eaccfca 100644 --- a/tests/baselines/reference/numericLiteralTypes3.types +++ b/tests/baselines/reference/numericLiteralTypes3.types @@ -118,9 +118,9 @@ function f4(a: A, b: B, c: C, d: D) { >c : C d = d; ->d = d : 1 | 2 +>d = d : D +>d : D >d : D ->d : 1 | 2 } function f5(a: A, b: B, c: C, d: D) { diff --git a/tests/cases/conformance/expressions/assignmentOperator/assignmentTypeNarrowing.ts b/tests/cases/conformance/expressions/assignmentOperator/assignmentTypeNarrowing.ts index 0e5e257635a..19d12a1b81a 100644 --- a/tests/cases/conformance/expressions/assignmentOperator/assignmentTypeNarrowing.ts +++ b/tests/cases/conformance/expressions/assignmentOperator/assignmentTypeNarrowing.ts @@ -26,3 +26,9 @@ let a: string[]; for (x of a) { x; // string } + +// Repro from #26405 + +type AOrArrA = T | T[]; +const arr: AOrArrA<{x?: "ok"}> = [{ x: "ok" }]; // weak type +arr.push({ x: "ok" }); From 435a12e476eefabb77451540839d150cef498408 Mon Sep 17 00:00:00 2001 From: Andy Date: Fri, 17 Aug 2018 18:41:18 -0700 Subject: [PATCH 2/6] Remove some duplicate code in convertToAsyncFunction unit tests (#26527) --- .../unittests/convertToAsyncFunction.ts | 34 +------------------ .../unittests/extractTestHelpers.ts | 2 +- 2 files changed, 2 insertions(+), 34 deletions(-) diff --git a/src/testRunner/unittests/convertToAsyncFunction.ts b/src/testRunner/unittests/convertToAsyncFunction.ts index 3b400fd0c2a..f39b8080ed2 100644 --- a/src/testRunner/unittests/convertToAsyncFunction.ts +++ b/src/testRunner/unittests/convertToAsyncFunction.ts @@ -319,38 +319,6 @@ interface String { charAt: any; } interface Array {}` }; - const newLineCharacter = "\n"; - const formatOptions: FormatCodeSettings = { - indentSize: 4, - tabSize: 4, - newLineCharacter, - convertTabsToSpaces: true, - indentStyle: IndentStyle.Smart, - insertSpaceAfterConstructor: false, - insertSpaceAfterCommaDelimiter: true, - insertSpaceAfterSemicolonInForStatements: true, - insertSpaceBeforeAndAfterBinaryOperators: true, - insertSpaceAfterKeywordsInControlFlowStatements: true, - insertSpaceAfterFunctionKeywordForAnonymousFunctions: false, - insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false, - insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false, - insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true, - insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false, - insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false, - insertSpaceBeforeFunctionParenthesis: false, - placeOpenBraceOnNewLineForFunctions: false, - placeOpenBraceOnNewLineForControlBlocks: false, - }; - - const notImplementedHost: LanguageServiceHost = { - getCompilationSettings: notImplemented, - getScriptFileNames: notImplemented, - getScriptVersion: notImplemented, - getScriptSnapshot: notImplemented, - getDefaultLibFileName: notImplemented, - getCurrentDirectory: notImplemented, - }; - function testConvertToAsyncFunction(caption: string, text: string, baselineFolder: string, description: DiagnosticMessage, includeLib?: boolean) { const t = getTest(text); const selectionRange = t.ranges.get("selection")!; @@ -389,7 +357,7 @@ interface Array {}` cancellationToken: { throwIfCancellationRequested: noop, isCancellationRequested: returnFalse }, preferences: emptyOptions, host: notImplementedHost, - formatContext: formatting.getFormatContext(formatOptions) + formatContext: formatting.getFormatContext(testFormatOptions) }; const diagnostics = languageService.getSuggestionDiagnostics(f.path); diff --git a/src/testRunner/unittests/extractTestHelpers.ts b/src/testRunner/unittests/extractTestHelpers.ts index 1b3d00362ec..6e46c1304ab 100644 --- a/src/testRunner/unittests/extractTestHelpers.ts +++ b/src/testRunner/unittests/extractTestHelpers.ts @@ -86,7 +86,7 @@ namespace ts { placeOpenBraceOnNewLineForControlBlocks: false, }; - const notImplementedHost: LanguageServiceHost = { + export const notImplementedHost: LanguageServiceHost = { getCompilationSettings: notImplemented, getScriptFileNames: notImplemented, getScriptVersion: notImplemented, From 1de8cd3f6295460d5e5a67070d5535780a2de1fe Mon Sep 17 00:00:00 2001 From: Ron Buckton Date: Mon, 20 Aug 2018 16:02:19 -0700 Subject: [PATCH 3/6] Emit lib reference directives in declaration output --- scripts/build/gulp-typescript-oop/protocol.js | 6 ++-- src/compiler/emitter.ts | 10 ++++-- src/compiler/program.ts | 1 + src/compiler/transformers/declarations.ts | 31 +++++++++++++++---- src/compiler/types.ts | 2 ++ .../reference/libReferenceDeclarationEmit.js | 25 +++++++++++++++ .../libReferenceDeclarationEmit.symbols | 13 ++++++++ .../libReferenceDeclarationEmit.types | 11 +++++++ .../libReferenceDeclarationEmitBundle.js | 30 ++++++++++++++++++ .../libReferenceDeclarationEmitBundle.symbols | 13 ++++++++ .../libReferenceDeclarationEmitBundle.types | 11 +++++++ .../libReferenceDeclarationEmit.ts | 12 +++++++ .../libReferenceDeclarationEmitBundle.ts | 13 ++++++++ 13 files changed, 167 insertions(+), 11 deletions(-) create mode 100644 tests/baselines/reference/libReferenceDeclarationEmit.js create mode 100644 tests/baselines/reference/libReferenceDeclarationEmit.symbols create mode 100644 tests/baselines/reference/libReferenceDeclarationEmit.types create mode 100644 tests/baselines/reference/libReferenceDeclarationEmitBundle.js create mode 100644 tests/baselines/reference/libReferenceDeclarationEmitBundle.symbols create mode 100644 tests/baselines/reference/libReferenceDeclarationEmitBundle.types create mode 100644 tests/cases/conformance/declarationEmit/libReferenceDeclarationEmit.ts create mode 100644 tests/cases/conformance/declarationEmit/libReferenceDeclarationEmitBundle.ts diff --git a/scripts/build/gulp-typescript-oop/protocol.js b/scripts/build/gulp-typescript-oop/protocol.js index 58e60b058d4..714b94f22a1 100644 --- a/scripts/build/gulp-typescript-oop/protocol.js +++ b/scripts/build/gulp-typescript-oop/protocol.js @@ -140,7 +140,7 @@ function diagnosticFromJson(json, host) { category: json.category, code: json.code, source: json.source, - relatedInformation: json.relatedInformation && json.relatedInformation.map(diagnosticRelatedInformationFromJson, host) + relatedInformation: json.relatedInformation && json.relatedInformation.map(json => diagnosticRelatedInformationFromJson(json, host)) }); } exports.diagnosticFromJson = diagnosticFromJson; @@ -169,7 +169,9 @@ function diagnosticRelatedInformationFromJson(json, host) { file: json.file && sourceFileFromJson(json.file, host), start: json.start, length: json.length, - messageText: json.messageText + messageText: json.messageText, + category: json.category, + code: json.code }; } exports.diagnosticRelatedInformationFromJson = diagnosticRelatedInformationFromJson; diff --git a/src/compiler/emitter.ts b/src/compiler/emitter.ts index badbfc4fa3f..613fe7f5c84 100644 --- a/src/compiler/emitter.ts +++ b/src/compiler/emitter.ts @@ -2593,14 +2593,14 @@ namespace ts { } function emitSyntheticTripleSlashReferencesIfNeeded(node: Bundle) { - emitTripleSlashDirectives(!!node.hasNoDefaultLib, node.syntheticFileReferences || [], node.syntheticTypeReferences || []); + emitTripleSlashDirectives(!!node.hasNoDefaultLib, node.syntheticFileReferences || [], node.syntheticTypeReferences || [], node.syntheticLibReferences || []); } function emitTripleSlashDirectivesIfNeeded(node: SourceFile) { - if (node.isDeclarationFile) emitTripleSlashDirectives(node.hasNoDefaultLib, node.referencedFiles, node.typeReferenceDirectives); + if (node.isDeclarationFile) emitTripleSlashDirectives(node.hasNoDefaultLib, node.referencedFiles, node.typeReferenceDirectives, node.libReferenceDirectives); } - function emitTripleSlashDirectives(hasNoDefaultLib: boolean, files: ReadonlyArray, types: ReadonlyArray) { + function emitTripleSlashDirectives(hasNoDefaultLib: boolean, files: ReadonlyArray, types: ReadonlyArray, libs: ReadonlyArray) { if (hasNoDefaultLib) { write(`/// `); writeLine(); @@ -2628,6 +2628,10 @@ namespace ts { write(`/// `); writeLine(); } + for (const directive of libs) { + write(`/// `); + writeLine(); + } } function emitSourceFileWorker(node: SourceFile) { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index d52025f5a30..abbc935cf65 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -1237,6 +1237,7 @@ namespace ts { getSourceFile: program.getSourceFile, getSourceFileByPath: program.getSourceFileByPath, getSourceFiles: program.getSourceFiles, + getLibFileFromReference: program.getLibFileFromReference, isSourceFileFromExternalLibrary, writeFile: writeFileCallback || ( (fileName, data, writeByteOrderMark, onError, sourceFiles) => host.writeFile(fileName, data, writeByteOrderMark, onError, sourceFiles)), diff --git a/src/compiler/transformers/declarations.ts b/src/compiler/transformers/declarations.ts index 2fa093128bc..063b5f1cd1d 100644 --- a/src/compiler/transformers/declarations.ts +++ b/src/compiler/transformers/declarations.ts @@ -33,7 +33,7 @@ namespace ts { let needsScopeFixMarker = false; let resultHasScopeMarker = false; let enclosingDeclaration: Node; - let necessaryTypeRefernces: Map | undefined; + let necessaryTypeReferences: Map | undefined; let lateMarkedStatements: LateVisibilityPaintedStatement[] | undefined; let lateStatementReplacementMap: Map>; let suppressNewDiagnosticContexts: boolean; @@ -53,6 +53,7 @@ namespace ts { let currentSourceFile: SourceFile; let refs: Map; + let libs: Map; const resolver = context.getEmitResolver(); const options = context.getCompilerOptions(); const newLine = getNewLineCharacter(options); @@ -63,9 +64,9 @@ namespace ts { if (!typeReferenceDirectives) { return; } - necessaryTypeRefernces = necessaryTypeRefernces || createMap(); + necessaryTypeReferences = necessaryTypeReferences || createMap(); for (const ref of typeReferenceDirectives) { - necessaryTypeRefernces.set(ref, true); + necessaryTypeReferences.set(ref, true); } } @@ -163,6 +164,7 @@ namespace ts { if (node.kind === SyntaxKind.Bundle) { isBundledEmit = true; refs = createMap(); + libs = createMap(); let hasNoDefaultLib = false; const bundle = createBundle(map(node.sourceFiles, sourceFile => { @@ -177,6 +179,7 @@ namespace ts { needsScopeFixMarker = false; resultHasScopeMarker = false; collectReferences(sourceFile, refs); + collectLibs(sourceFile, libs); if (isExternalModule(sourceFile)) { resultHasExternalModuleIndicator = false; // unused in external module bundle emit (all external modules are within module blocks, therefore are known to be modules) needsDeclare = false; @@ -200,6 +203,7 @@ namespace ts { })); bundle.syntheticFileReferences = []; bundle.syntheticTypeReferences = getFileReferencesForUsedTypeReferences(); + bundle.syntheticLibReferences = getLibReferences(); bundle.hasNoDefaultLib = hasNoDefaultLib; const outputFilePath = getDirectoryPath(normalizeSlashes(getOutputPathsFor(node, host, /*forceDtsPaths*/ true).declarationFilePath!)); const referenceVisitor = mapReferencesIntoArray(bundle.syntheticFileReferences as FileReference[], outputFilePath); @@ -219,8 +223,9 @@ namespace ts { suppressNewDiagnosticContexts = false; lateMarkedStatements = undefined; lateStatementReplacementMap = createMap(); - necessaryTypeRefernces = undefined; + necessaryTypeReferences = undefined; refs = collectReferences(currentSourceFile, createMap()); + libs = collectLibs(currentSourceFile, createMap()); const references: FileReference[] = []; const outputFilePath = getDirectoryPath(normalizeSlashes(getOutputPathsFor(node, host, /*forceDtsPaths*/ true).declarationFilePath!)); const referenceVisitor = mapReferencesIntoArray(references, outputFilePath); @@ -231,12 +236,16 @@ namespace ts { if (isExternalModule(node) && (!resultHasExternalModuleIndicator || (needsScopeFixMarker && !resultHasScopeMarker))) { combinedStatements = setTextRange(createNodeArray([...combinedStatements, createExportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createNamedExports([]), /*moduleSpecifier*/ undefined)]), combinedStatements); } - const updated = updateSourceFileNode(node, combinedStatements, /*isDeclarationFile*/ true, references, getFileReferencesForUsedTypeReferences(), node.hasNoDefaultLib); + const updated = updateSourceFileNode(node, combinedStatements, /*isDeclarationFile*/ true, references, getFileReferencesForUsedTypeReferences(), node.hasNoDefaultLib, getLibReferences()); updated.exportedModulesFromDeclarationEmit = exportedModulesFromDeclarationEmit; return updated; + function getLibReferences() { + return map(arrayFrom(libs.keys()), lib => ({ fileName: lib, pos: -1, end: -1 })); + } + function getFileReferencesForUsedTypeReferences() { - return necessaryTypeRefernces ? mapDefined(arrayFrom(necessaryTypeRefernces.keys()), getFileReferenceForTypeName) : []; + return necessaryTypeReferences ? mapDefined(arrayFrom(necessaryTypeReferences.keys()), getFileReferenceForTypeName) : []; } function getFileReferenceForTypeName(typeName: string): FileReference | undefined { @@ -297,6 +306,16 @@ namespace ts { return ret; } + function collectLibs(sourceFile: SourceFile, ret: Map) { + forEach(sourceFile.libReferenceDirectives, ref => { + const lib = host.getLibFileFromReference(ref); + if (lib) { + ret.set(ref.fileName.toLocaleLowerCase(), true); + } + }); + return ret; + } + function filterBindingPatternInitializers(name: BindingName) { if (name.kind === SyntaxKind.Identifier) { return name; diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 8696512071d..4330a776d04 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2648,6 +2648,7 @@ namespace ts { sourceFiles: ReadonlyArray; /* @internal */ syntheticFileReferences?: ReadonlyArray; /* @internal */ syntheticTypeReferences?: ReadonlyArray; + /* @internal */ syntheticLibReferences?: ReadonlyArray; /* @internal */ hasNoDefaultLib?: boolean; } @@ -5074,6 +5075,7 @@ namespace ts { /* @internal */ isSourceFileFromExternalLibrary(file: SourceFile): boolean; + getLibFileFromReference(ref: FileReference): SourceFile | undefined; getCommonSourceDirectory(): string; getCanonicalFileName(fileName: string): string; diff --git a/tests/baselines/reference/libReferenceDeclarationEmit.js b/tests/baselines/reference/libReferenceDeclarationEmit.js new file mode 100644 index 00000000000..83082c3ee98 --- /dev/null +++ b/tests/baselines/reference/libReferenceDeclarationEmit.js @@ -0,0 +1,25 @@ +//// [tests/cases/conformance/declarationEmit/libReferenceDeclarationEmit.ts] //// + +//// [file1.ts] +/// +export declare const elem: HTMLElement; + +//// [file2.ts] +/// +export {} +declare const elem: HTMLElement; + +//// [file1.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); +//// [file2.js] +"use strict"; +Object.defineProperty(exports, "__esModule", { value: true }); + + +//// [file1.d.ts] +/// +export declare const elem: HTMLElement; +//// [file2.d.ts] +/// +export {}; diff --git a/tests/baselines/reference/libReferenceDeclarationEmit.symbols b/tests/baselines/reference/libReferenceDeclarationEmit.symbols new file mode 100644 index 00000000000..3753ee0b80d --- /dev/null +++ b/tests/baselines/reference/libReferenceDeclarationEmit.symbols @@ -0,0 +1,13 @@ +=== tests/cases/conformance/declarationEmit/file1.ts === +/// +export declare const elem: HTMLElement; +>elem : Symbol(elem, Decl(file1.ts, 1, 20)) +>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) + +=== tests/cases/conformance/declarationEmit/file2.ts === +/// +export {} +declare const elem: HTMLElement; +>elem : Symbol(elem, Decl(file2.ts, 2, 13)) +>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) + diff --git a/tests/baselines/reference/libReferenceDeclarationEmit.types b/tests/baselines/reference/libReferenceDeclarationEmit.types new file mode 100644 index 00000000000..34102b07321 --- /dev/null +++ b/tests/baselines/reference/libReferenceDeclarationEmit.types @@ -0,0 +1,11 @@ +=== tests/cases/conformance/declarationEmit/file1.ts === +/// +export declare const elem: HTMLElement; +>elem : HTMLElement + +=== tests/cases/conformance/declarationEmit/file2.ts === +/// +export {} +declare const elem: HTMLElement; +>elem : HTMLElement + diff --git a/tests/baselines/reference/libReferenceDeclarationEmitBundle.js b/tests/baselines/reference/libReferenceDeclarationEmitBundle.js new file mode 100644 index 00000000000..03474372f87 --- /dev/null +++ b/tests/baselines/reference/libReferenceDeclarationEmitBundle.js @@ -0,0 +1,30 @@ +//// [tests/cases/conformance/declarationEmit/libReferenceDeclarationEmitBundle.ts] //// + +//// [file1.ts] +/// +export declare const elem: HTMLElement; + +//// [file2.ts] +/// +export {} +declare const elem: HTMLElement; + +//// [bundle.js] +define("file1", ["require", "exports"], function (require, exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); +}); +define("file2", ["require", "exports"], function (require, exports) { + "use strict"; + Object.defineProperty(exports, "__esModule", { value: true }); +}); + + +//// [bundle.d.ts] +/// +declare module "file1" { + export const elem: HTMLElement; +} +declare module "file2" { + export {}; +} diff --git a/tests/baselines/reference/libReferenceDeclarationEmitBundle.symbols b/tests/baselines/reference/libReferenceDeclarationEmitBundle.symbols new file mode 100644 index 00000000000..3753ee0b80d --- /dev/null +++ b/tests/baselines/reference/libReferenceDeclarationEmitBundle.symbols @@ -0,0 +1,13 @@ +=== tests/cases/conformance/declarationEmit/file1.ts === +/// +export declare const elem: HTMLElement; +>elem : Symbol(elem, Decl(file1.ts, 1, 20)) +>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) + +=== tests/cases/conformance/declarationEmit/file2.ts === +/// +export {} +declare const elem: HTMLElement; +>elem : Symbol(elem, Decl(file2.ts, 2, 13)) +>HTMLElement : Symbol(HTMLElement, Decl(lib.dom.d.ts, --, --), Decl(lib.dom.d.ts, --, --)) + diff --git a/tests/baselines/reference/libReferenceDeclarationEmitBundle.types b/tests/baselines/reference/libReferenceDeclarationEmitBundle.types new file mode 100644 index 00000000000..34102b07321 --- /dev/null +++ b/tests/baselines/reference/libReferenceDeclarationEmitBundle.types @@ -0,0 +1,11 @@ +=== tests/cases/conformance/declarationEmit/file1.ts === +/// +export declare const elem: HTMLElement; +>elem : HTMLElement + +=== tests/cases/conformance/declarationEmit/file2.ts === +/// +export {} +declare const elem: HTMLElement; +>elem : HTMLElement + diff --git a/tests/cases/conformance/declarationEmit/libReferenceDeclarationEmit.ts b/tests/cases/conformance/declarationEmit/libReferenceDeclarationEmit.ts new file mode 100644 index 00000000000..2afc4290776 --- /dev/null +++ b/tests/cases/conformance/declarationEmit/libReferenceDeclarationEmit.ts @@ -0,0 +1,12 @@ +// @target: esnext +// @module: commonjs +// @lib: esnext +// @declaration: true +// @filename: file1.ts +/// +export declare const elem: HTMLElement; + +// @filename: file2.ts +/// +export {} +declare const elem: HTMLElement; \ No newline at end of file diff --git a/tests/cases/conformance/declarationEmit/libReferenceDeclarationEmitBundle.ts b/tests/cases/conformance/declarationEmit/libReferenceDeclarationEmitBundle.ts new file mode 100644 index 00000000000..e379567d559 --- /dev/null +++ b/tests/cases/conformance/declarationEmit/libReferenceDeclarationEmitBundle.ts @@ -0,0 +1,13 @@ +// @target: esnext +// @module: amd +// @lib: esnext +// @declaration: true +// @outFile: bundle.js +// @filename: file1.ts +/// +export declare const elem: HTMLElement; + +// @filename: file2.ts +/// +export {} +declare const elem: HTMLElement; \ No newline at end of file From d00ad19bd7a5f0b6934c53d40c3021bb7b1a4803 Mon Sep 17 00:00:00 2001 From: David Sherret Date: Mon, 20 Aug 2018 20:43:36 -0400 Subject: [PATCH 4/6] Provide a default script target for the document registry. --- src/services/documentRegistry.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/documentRegistry.ts b/src/services/documentRegistry.ts index ee99aed930c..574125f6ac0 100644 --- a/src/services/documentRegistry.ts +++ b/src/services/documentRegistry.ts @@ -184,7 +184,7 @@ namespace ts { const bucket = getBucketForCompilationSettings(key, /*createIfMissing*/ true); let entry = bucket.get(path); - const scriptTarget = scriptKind === ScriptKind.JSON ? ScriptTarget.JSON : compilationSettings.target; + const scriptTarget = scriptKind === ScriptKind.JSON ? ScriptTarget.JSON : compilationSettings.target || ScriptTarget.ES5; if (!entry && externalCache) { const sourceFile = externalCache.getDocument(key, path); if (sourceFile) { @@ -199,7 +199,7 @@ namespace ts { if (!entry) { // Have never seen this file with these settings. Create a new source file for it. - const sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, scriptTarget!, version, /*setNodeParents*/ false, scriptKind); // TODO: GH#18217 + const sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, scriptTarget, version, /*setNodeParents*/ false, scriptKind); // TODO: GH#18217 if (externalCache) { externalCache.setDocument(key, path, sourceFile); } From 72886512a6fd6865e2a5873833a8ca7638a51c6e Mon Sep 17 00:00:00 2001 From: Andy Date: Tue, 21 Aug 2018 10:02:02 -0700 Subject: [PATCH 5/6] When --noImplicitAny is enabled, don't report errors suggesting that a 'void' function can be 'new'ed (#26579) --- src/compiler/checker.ts | 12 +++++---- ...peratorErrorCases_noImplicitAny.errors.txt | 21 ++++++++++++++++ .../newOperatorErrorCases_noImplicitAny.js | 18 +++++++++++++ ...ewOperatorErrorCases_noImplicitAny.symbols | 21 ++++++++++++++++ .../newOperatorErrorCases_noImplicitAny.types | 25 +++++++++++++++++++ .../newOperatorErrorCases_noImplicitAny.ts | 10 ++++++++ 6 files changed, 102 insertions(+), 5 deletions(-) create mode 100644 tests/baselines/reference/newOperatorErrorCases_noImplicitAny.errors.txt create mode 100644 tests/baselines/reference/newOperatorErrorCases_noImplicitAny.js create mode 100644 tests/baselines/reference/newOperatorErrorCases_noImplicitAny.symbols create mode 100644 tests/baselines/reference/newOperatorErrorCases_noImplicitAny.types create mode 100644 tests/cases/conformance/expressions/newOperator/newOperatorErrorCases_noImplicitAny.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 0b357bd771d..7fec31477c1 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -19458,11 +19458,13 @@ namespace ts { const callSignatures = getSignaturesOfType(expressionType, SignatureKind.Call); if (callSignatures.length) { const signature = resolveCall(node, callSignatures, candidatesOutArray, isForSignatureHelp); - if (signature.declaration && !isJavascriptConstructor(signature.declaration) && getReturnTypeOfSignature(signature) !== voidType) { - error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword); - } - if (getThisTypeOfSignature(signature) === voidType) { - error(node, Diagnostics.A_function_that_is_called_with_the_new_keyword_cannot_have_a_this_type_that_is_void); + if (!noImplicitAny) { + if (signature.declaration && !isJavascriptConstructor(signature.declaration) && getReturnTypeOfSignature(signature) !== voidType) { + error(node, Diagnostics.Only_a_void_function_can_be_called_with_the_new_keyword); + } + if (getThisTypeOfSignature(signature) === voidType) { + error(node, Diagnostics.A_function_that_is_called_with_the_new_keyword_cannot_have_a_this_type_that_is_void); + } } return signature; } diff --git a/tests/baselines/reference/newOperatorErrorCases_noImplicitAny.errors.txt b/tests/baselines/reference/newOperatorErrorCases_noImplicitAny.errors.txt new file mode 100644 index 00000000000..3c131f4136f --- /dev/null +++ b/tests/baselines/reference/newOperatorErrorCases_noImplicitAny.errors.txt @@ -0,0 +1,21 @@ +tests/cases/conformance/expressions/newOperator/newOperatorErrorCases_noImplicitAny.ts(2,1): error TS7009: 'new' expression, whose target lacks a construct signature, implicitly has an 'any' type. +tests/cases/conformance/expressions/newOperator/newOperatorErrorCases_noImplicitAny.ts(5,1): error TS7009: 'new' expression, whose target lacks a construct signature, implicitly has an 'any' type. +tests/cases/conformance/expressions/newOperator/newOperatorErrorCases_noImplicitAny.ts(8,1): error TS7009: 'new' expression, whose target lacks a construct signature, implicitly has an 'any' type. + + +==== tests/cases/conformance/expressions/newOperator/newOperatorErrorCases_noImplicitAny.ts (3 errors) ==== + function fnNumber(this: void): number { return 90; } + new fnNumber(); // Error + ~~~~~~~~~~~~~~ +!!! error TS7009: 'new' expression, whose target lacks a construct signature, implicitly has an 'any' type. + + function fnVoid(this: void): void {} + new fnVoid(); // Error + ~~~~~~~~~~~~ +!!! error TS7009: 'new' expression, whose target lacks a construct signature, implicitly has an 'any' type. + + function functionVoidNoThis(): void {} + new functionVoidNoThis(); // Error + ~~~~~~~~~~~~~~~~~~~~~~~~ +!!! error TS7009: 'new' expression, whose target lacks a construct signature, implicitly has an 'any' type. + \ No newline at end of file diff --git a/tests/baselines/reference/newOperatorErrorCases_noImplicitAny.js b/tests/baselines/reference/newOperatorErrorCases_noImplicitAny.js new file mode 100644 index 00000000000..935c2ba036f --- /dev/null +++ b/tests/baselines/reference/newOperatorErrorCases_noImplicitAny.js @@ -0,0 +1,18 @@ +//// [newOperatorErrorCases_noImplicitAny.ts] +function fnNumber(this: void): number { return 90; } +new fnNumber(); // Error + +function fnVoid(this: void): void {} +new fnVoid(); // Error + +function functionVoidNoThis(): void {} +new functionVoidNoThis(); // Error + + +//// [newOperatorErrorCases_noImplicitAny.js] +function fnNumber() { return 90; } +new fnNumber(); // Error +function fnVoid() { } +new fnVoid(); // Error +function functionVoidNoThis() { } +new functionVoidNoThis(); // Error diff --git a/tests/baselines/reference/newOperatorErrorCases_noImplicitAny.symbols b/tests/baselines/reference/newOperatorErrorCases_noImplicitAny.symbols new file mode 100644 index 00000000000..dadb2379223 --- /dev/null +++ b/tests/baselines/reference/newOperatorErrorCases_noImplicitAny.symbols @@ -0,0 +1,21 @@ +=== tests/cases/conformance/expressions/newOperator/newOperatorErrorCases_noImplicitAny.ts === +function fnNumber(this: void): number { return 90; } +>fnNumber : Symbol(fnNumber, Decl(newOperatorErrorCases_noImplicitAny.ts, 0, 0)) +>this : Symbol(this, Decl(newOperatorErrorCases_noImplicitAny.ts, 0, 18)) + +new fnNumber(); // Error +>fnNumber : Symbol(fnNumber, Decl(newOperatorErrorCases_noImplicitAny.ts, 0, 0)) + +function fnVoid(this: void): void {} +>fnVoid : Symbol(fnVoid, Decl(newOperatorErrorCases_noImplicitAny.ts, 1, 15)) +>this : Symbol(this, Decl(newOperatorErrorCases_noImplicitAny.ts, 3, 16)) + +new fnVoid(); // Error +>fnVoid : Symbol(fnVoid, Decl(newOperatorErrorCases_noImplicitAny.ts, 1, 15)) + +function functionVoidNoThis(): void {} +>functionVoidNoThis : Symbol(functionVoidNoThis, Decl(newOperatorErrorCases_noImplicitAny.ts, 4, 13)) + +new functionVoidNoThis(); // Error +>functionVoidNoThis : Symbol(functionVoidNoThis, Decl(newOperatorErrorCases_noImplicitAny.ts, 4, 13)) + diff --git a/tests/baselines/reference/newOperatorErrorCases_noImplicitAny.types b/tests/baselines/reference/newOperatorErrorCases_noImplicitAny.types new file mode 100644 index 00000000000..bd1d8082aae --- /dev/null +++ b/tests/baselines/reference/newOperatorErrorCases_noImplicitAny.types @@ -0,0 +1,25 @@ +=== tests/cases/conformance/expressions/newOperator/newOperatorErrorCases_noImplicitAny.ts === +function fnNumber(this: void): number { return 90; } +>fnNumber : (this: void) => number +>this : void +>90 : 90 + +new fnNumber(); // Error +>new fnNumber() : any +>fnNumber : (this: void) => number + +function fnVoid(this: void): void {} +>fnVoid : (this: void) => void +>this : void + +new fnVoid(); // Error +>new fnVoid() : any +>fnVoid : (this: void) => void + +function functionVoidNoThis(): void {} +>functionVoidNoThis : () => void + +new functionVoidNoThis(); // Error +>new functionVoidNoThis() : any +>functionVoidNoThis : () => void + diff --git a/tests/cases/conformance/expressions/newOperator/newOperatorErrorCases_noImplicitAny.ts b/tests/cases/conformance/expressions/newOperator/newOperatorErrorCases_noImplicitAny.ts new file mode 100644 index 00000000000..9b3b2c091d6 --- /dev/null +++ b/tests/cases/conformance/expressions/newOperator/newOperatorErrorCases_noImplicitAny.ts @@ -0,0 +1,10 @@ +// @noImplicitAny: true + +function fnNumber(this: void): number { return 90; } +new fnNumber(); // Error + +function fnVoid(this: void): void {} +new fnVoid(); // Error + +function functionVoidNoThis(): void {} +new functionVoidNoThis(); // Error From 0baf7465a56ecf1b378fde67e2356c95f6c8e8fe Mon Sep 17 00:00:00 2001 From: David Sherret Date: Tue, 21 Aug 2018 13:22:22 -0400 Subject: [PATCH 6/6] Remove todo. --- src/services/documentRegistry.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/documentRegistry.ts b/src/services/documentRegistry.ts index 574125f6ac0..7a21cebb4f3 100644 --- a/src/services/documentRegistry.ts +++ b/src/services/documentRegistry.ts @@ -199,7 +199,7 @@ namespace ts { if (!entry) { // Have never seen this file with these settings. Create a new source file for it. - const sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, scriptTarget, version, /*setNodeParents*/ false, scriptKind); // TODO: GH#18217 + const sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, scriptTarget, version, /*setNodeParents*/ false, scriptKind); if (externalCache) { externalCache.setDocument(key, path, sourceFile); }