From ac4490c0dedd77ee68349ee73d0e0ab8f58091c6 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Thu, 8 Oct 2020 16:43:00 -0700 Subject: [PATCH 1/8] Add test case for 'useUnknownInCatchVariables'. --- .../compiler/useUnknownInCatchVariables01.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 tests/cases/compiler/useUnknownInCatchVariables01.ts diff --git a/tests/cases/compiler/useUnknownInCatchVariables01.ts b/tests/cases/compiler/useUnknownInCatchVariables01.ts new file mode 100644 index 0000000000000..c9633c97e4fcc --- /dev/null +++ b/tests/cases/compiler/useUnknownInCatchVariables01.ts @@ -0,0 +1,23 @@ +// @useUnknownInCatchVariables: true + +try { + // ... +} +catch (e) { + // error! + void e.toUpperCase(); + void e++; + void e(); + + if (typeof e === "string") { + // works! + // We've narrowed 'e' down to the type 'string'. + console.log(e.toUpperCase()); + } + if (e instanceof Error) { + e.stack?.toUpperCase(); + } + if (typeof e === "number") { + e++; + } +} \ No newline at end of file From 07a9e495e7eaaf3945720d55f9e8a22ea0974e31 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 5 May 2021 21:32:11 +0000 Subject: [PATCH 2/8] Add new 'useUnknownInCatchVariables' flag. --- src/compiler/checker.ts | 2 +- src/compiler/commandLineParser.ts | 8 ++++++++ src/compiler/diagnosticMessages.json | 4 ++++ src/compiler/types.ts | 1 + 4 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4622e53b001f6..3afe6983c6607 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -8929,7 +8929,7 @@ namespace ts { if (isCatchClauseVariableDeclarationOrBindingElement(declaration)) { const typeNode = getEffectiveTypeAnnotationNode(declaration); if (typeNode === undefined) { - return anyType; + return compilerOptions.useUnknownInCatchVariables ? unknownType : anyType; } const type = getTypeOfNode(typeNode); // an errorType will make `checkTryStatement` issue an error diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 253e38a5295b5..819be6fadce94 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -688,6 +688,14 @@ namespace ts { category: Diagnostics.Additional_Checks, description: Diagnostics.Require_undeclared_properties_from_index_signatures_to_use_element_accesses }, + { + name: "useUnknownInCatchVariables", + type: "boolean", + affectsSemanticDiagnostics: true, + showInSimplifiedHelpView: true, + category: Diagnostics.Additional_Checks, + description: Diagnostics.Type_catch_clause_variables_as_unknown_instead_of_any, + }, // Module Resolution { diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index f4fbe932e93f9..ffa29e0826123 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -5095,6 +5095,10 @@ "category": "Message", "code": 6802 }, + "Type catch clause variables as 'unknown' instead of 'any'.": { + "category": "Message", + "code": 6803 + }, "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", diff --git a/src/compiler/types.ts b/src/compiler/types.ts index f7183d7dd6c27..acb17300e36d1 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -6006,6 +6006,7 @@ namespace ts { /* @internal */ suppressOutputPathCheck?: boolean; target?: ScriptTarget; // TODO: GH#18217 frequently asserted as defined traceResolution?: boolean; + useUnknownInCatchVariables?: boolean; resolveJsonModule?: boolean; types?: string[]; /** Paths used to compute primary types search locations */ From 4f0baa0ea89c259bee92aaa11a5304a2c2e4c37a Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Thu, 8 Oct 2020 18:07:38 -0700 Subject: [PATCH 3/8] Accepted baselines. --- .../reference/api/tsserverlibrary.d.ts | 1 + tests/baselines/reference/api/typescript.d.ts | 1 + .../useUnknownInCatchVariables/tsconfig.json | 5 ++ .../tsconfig.json | 1 + .../tsconfig.json | 1 + .../tsconfig.json | 1 + .../tsconfig.json | 1 + .../tsconfig.json | 1 + .../tsconfig.json | 1 + .../tsconfig.json | 1 + .../tsconfig.json | 1 + .../tsconfig.json | 1 + .../declarationDir-is-specified.js | 1 + ...-outDir-and-declarationDir-is-specified.js | 1 + .../when-outDir-is-specified.js | 1 + .../with-outFile.js | 1 + ...e-is-specified-with-declaration-enabled.js | 1 + .../without-outDir-or-outFile-is-specified.js | 1 + .../useUnknownInCatchVariables01.errors.txt | 35 ++++++++++ .../reference/useUnknownInCatchVariables01.js | 45 +++++++++++++ .../useUnknownInCatchVariables01.symbols | 48 +++++++++++++ .../useUnknownInCatchVariables01.types | 67 +++++++++++++++++++ 22 files changed, 217 insertions(+) create mode 100644 tests/baselines/reference/showConfig/Shows tsconfig for single option/useUnknownInCatchVariables/tsconfig.json create mode 100644 tests/baselines/reference/useUnknownInCatchVariables01.errors.txt create mode 100644 tests/baselines/reference/useUnknownInCatchVariables01.js create mode 100644 tests/baselines/reference/useUnknownInCatchVariables01.symbols create mode 100644 tests/baselines/reference/useUnknownInCatchVariables01.types diff --git a/tests/baselines/reference/api/tsserverlibrary.d.ts b/tests/baselines/reference/api/tsserverlibrary.d.ts index e94b1da823f34..7b01b03a8ed39 100644 --- a/tests/baselines/reference/api/tsserverlibrary.d.ts +++ b/tests/baselines/reference/api/tsserverlibrary.d.ts @@ -2896,6 +2896,7 @@ declare namespace ts { suppressImplicitAnyIndexErrors?: boolean; target?: ScriptTarget; traceResolution?: boolean; + useUnknownInCatchVariables?: boolean; resolveJsonModule?: boolean; types?: string[]; /** Paths used to compute primary types search locations */ diff --git a/tests/baselines/reference/api/typescript.d.ts b/tests/baselines/reference/api/typescript.d.ts index 7b8a24ba088b4..7eb229cd26f21 100644 --- a/tests/baselines/reference/api/typescript.d.ts +++ b/tests/baselines/reference/api/typescript.d.ts @@ -2896,6 +2896,7 @@ declare namespace ts { suppressImplicitAnyIndexErrors?: boolean; target?: ScriptTarget; traceResolution?: boolean; + useUnknownInCatchVariables?: boolean; resolveJsonModule?: boolean; types?: string[]; /** Paths used to compute primary types search locations */ diff --git a/tests/baselines/reference/showConfig/Shows tsconfig for single option/useUnknownInCatchVariables/tsconfig.json b/tests/baselines/reference/showConfig/Shows tsconfig for single option/useUnknownInCatchVariables/tsconfig.json new file mode 100644 index 0000000000000..19c7c22b6926d --- /dev/null +++ b/tests/baselines/reference/showConfig/Shows tsconfig for single option/useUnknownInCatchVariables/tsconfig.json @@ -0,0 +1,5 @@ +{ + "compilerOptions": { + "useUnknownInCatchVariables": true + } +} diff --git a/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json index 194d0d139323d..d6608aca93f1a 100644 --- a/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Default initialized TSConfig/tsconfig.json @@ -42,6 +42,7 @@ // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with advanced options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with advanced options/tsconfig.json index 6cd483e76a9f0..3ef4b166551d9 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with advanced options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with advanced options/tsconfig.json @@ -42,6 +42,7 @@ // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json index d10611ebdae17..f04666ebe1bc9 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with boolean value compiler options/tsconfig.json @@ -42,6 +42,7 @@ // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json index 485a029ae8329..2e7c979133241 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with enum value compiler options/tsconfig.json @@ -42,6 +42,7 @@ // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json index 66028174a9ea6..3803ca2ff3c4a 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with files options/tsconfig.json @@ -42,6 +42,7 @@ // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json index d926a958770fe..c25bf0ca0cf64 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option value/tsconfig.json @@ -42,6 +42,7 @@ // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json index 194d0d139323d..d6608aca93f1a 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with incorrect compiler option/tsconfig.json @@ -42,6 +42,7 @@ // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json index 9c4a96d98d35c..4ef16804d93cc 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options with enum value/tsconfig.json @@ -42,6 +42,7 @@ // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json index dd5852675808c..e87d2b3c1e51a 100644 --- a/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json +++ b/tests/baselines/reference/tsConfig/Initialized TSConfig with list compiler options/tsconfig.json @@ -42,6 +42,7 @@ // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/declarationDir-is-specified.js b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/declarationDir-is-specified.js index 16b925d5936d4..29adfeda75ead 100644 --- a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/declarationDir-is-specified.js +++ b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/declarationDir-is-specified.js @@ -63,6 +63,7 @@ interface Array { length: number; [n: number]: T; } // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/when-outDir-and-declarationDir-is-specified.js b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/when-outDir-and-declarationDir-is-specified.js index 7ce20f4eb951f..e60d85aa9cdd7 100644 --- a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/when-outDir-and-declarationDir-is-specified.js +++ b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/when-outDir-and-declarationDir-is-specified.js @@ -63,6 +63,7 @@ interface Array { length: number; [n: number]: T; } // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/when-outDir-is-specified.js b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/when-outDir-is-specified.js index f35e860335bb8..0b76d527167c5 100644 --- a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/when-outDir-is-specified.js +++ b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/when-outDir-is-specified.js @@ -63,6 +63,7 @@ interface Array { length: number; [n: number]: T; } // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/with-outFile.js b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/with-outFile.js index 8f6c7e53cc61b..88a6158042a45 100644 --- a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/with-outFile.js +++ b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/with-outFile.js @@ -63,6 +63,7 @@ interface Array { length: number; [n: number]: T; } // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/without-outDir-or-outFile-is-specified-with-declaration-enabled.js b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/without-outDir-or-outFile-is-specified-with-declaration-enabled.js index 0bc0a489d1891..5a500b722cecf 100644 --- a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/without-outDir-or-outFile-is-specified-with-declaration-enabled.js +++ b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/without-outDir-or-outFile-is-specified-with-declaration-enabled.js @@ -63,6 +63,7 @@ interface Array { length: number; [n: number]: T; } // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/without-outDir-or-outFile-is-specified.js b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/without-outDir-or-outFile-is-specified.js index 79c6b55b1f1e0..7dda7a0b7d5b4 100644 --- a/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/without-outDir-or-outFile-is-specified.js +++ b/tests/baselines/reference/tscWatch/programUpdates/should-not-trigger-recompilation-because-of-program-emit/without-outDir-or-outFile-is-specified.js @@ -63,6 +63,7 @@ interface Array { length: number; [n: number]: T; } // "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */ // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */ // "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */ + // "useUnknownInCatchVariables": true, /* Type catch clause variables as 'unknown' instead of 'any'. */ /* Module Resolution Options */ // "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */ diff --git a/tests/baselines/reference/useUnknownInCatchVariables01.errors.txt b/tests/baselines/reference/useUnknownInCatchVariables01.errors.txt new file mode 100644 index 0000000000000..21f7d8ba35499 --- /dev/null +++ b/tests/baselines/reference/useUnknownInCatchVariables01.errors.txt @@ -0,0 +1,35 @@ +tests/cases/compiler/useUnknownInCatchVariables01.ts(6,12): error TS2339: Property 'toUpperCase' does not exist on type 'unknown'. +tests/cases/compiler/useUnknownInCatchVariables01.ts(7,10): error TS2356: An arithmetic operand must be of type 'any', 'number', 'bigint' or an enum type. +tests/cases/compiler/useUnknownInCatchVariables01.ts(8,10): error TS2349: This expression is not callable. + Type '{}' has no call signatures. + + +==== tests/cases/compiler/useUnknownInCatchVariables01.ts (3 errors) ==== + try { + // ... + } + catch (e) { + // error! + void e.toUpperCase(); + ~~~~~~~~~~~ +!!! error TS2339: Property 'toUpperCase' does not exist on type 'unknown'. + void e++; + ~ +!!! error TS2356: An arithmetic operand must be of type 'any', 'number', 'bigint' or an enum type. + void e(); + ~ +!!! error TS2349: This expression is not callable. +!!! error TS2349: Type '{}' has no call signatures. + + if (typeof e === "string") { + // works! + // We've narrowed 'e' down to the type 'string'. + console.log(e.toUpperCase()); + } + if (e instanceof Error) { + e.stack?.toUpperCase(); + } + if (typeof e === "number") { + e++; + } + } \ No newline at end of file diff --git a/tests/baselines/reference/useUnknownInCatchVariables01.js b/tests/baselines/reference/useUnknownInCatchVariables01.js new file mode 100644 index 0000000000000..85dc03e4fb033 --- /dev/null +++ b/tests/baselines/reference/useUnknownInCatchVariables01.js @@ -0,0 +1,45 @@ +//// [useUnknownInCatchVariables01.ts] +try { + // ... +} +catch (e) { + // error! + void e.toUpperCase(); + void e++; + void e(); + + if (typeof e === "string") { + // works! + // We've narrowed 'e' down to the type 'string'. + console.log(e.toUpperCase()); + } + if (e instanceof Error) { + e.stack?.toUpperCase(); + } + if (typeof e === "number") { + e++; + } +} + +//// [useUnknownInCatchVariables01.js] +var _a; +try { + // ... +} +catch (e) { + // error! + void e.toUpperCase(); + void e++; + void e(); + if (typeof e === "string") { + // works! + // We've narrowed 'e' down to the type 'string'. + console.log(e.toUpperCase()); + } + if (e instanceof Error) { + (_a = e.stack) === null || _a === void 0 ? void 0 : _a.toUpperCase(); + } + if (typeof e === "number") { + e++; + } +} diff --git a/tests/baselines/reference/useUnknownInCatchVariables01.symbols b/tests/baselines/reference/useUnknownInCatchVariables01.symbols new file mode 100644 index 0000000000000..0511ec3daac7d --- /dev/null +++ b/tests/baselines/reference/useUnknownInCatchVariables01.symbols @@ -0,0 +1,48 @@ +=== tests/cases/compiler/useUnknownInCatchVariables01.ts === +try { + // ... +} +catch (e) { +>e : Symbol(e, Decl(useUnknownInCatchVariables01.ts, 3, 7)) + + // error! + void e.toUpperCase(); +>e : Symbol(e, Decl(useUnknownInCatchVariables01.ts, 3, 7)) + + void e++; +>e : Symbol(e, Decl(useUnknownInCatchVariables01.ts, 3, 7)) + + void e(); +>e : Symbol(e, Decl(useUnknownInCatchVariables01.ts, 3, 7)) + + if (typeof e === "string") { +>e : Symbol(e, Decl(useUnknownInCatchVariables01.ts, 3, 7)) + + // works! + // We've narrowed 'e' down to the type 'string'. + console.log(e.toUpperCase()); +>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>console : Symbol(console, Decl(lib.dom.d.ts, --, --)) +>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --)) +>e.toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) +>e : Symbol(e, Decl(useUnknownInCatchVariables01.ts, 3, 7)) +>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) + } + if (e instanceof Error) { +>e : Symbol(e, Decl(useUnknownInCatchVariables01.ts, 3, 7)) +>Error : Symbol(Error, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + + e.stack?.toUpperCase(); +>e.stack?.toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) +>e.stack : Symbol(Error.stack, Decl(lib.es5.d.ts, --, --)) +>e : Symbol(e, Decl(useUnknownInCatchVariables01.ts, 3, 7)) +>stack : Symbol(Error.stack, Decl(lib.es5.d.ts, --, --)) +>toUpperCase : Symbol(String.toUpperCase, Decl(lib.es5.d.ts, --, --)) + } + if (typeof e === "number") { +>e : Symbol(e, Decl(useUnknownInCatchVariables01.ts, 3, 7)) + + e++; +>e : Symbol(e, Decl(useUnknownInCatchVariables01.ts, 3, 7)) + } +} diff --git a/tests/baselines/reference/useUnknownInCatchVariables01.types b/tests/baselines/reference/useUnknownInCatchVariables01.types new file mode 100644 index 0000000000000..f818272aefaa4 --- /dev/null +++ b/tests/baselines/reference/useUnknownInCatchVariables01.types @@ -0,0 +1,67 @@ +=== tests/cases/compiler/useUnknownInCatchVariables01.ts === +try { + // ... +} +catch (e) { +>e : unknown + + // error! + void e.toUpperCase(); +>void e.toUpperCase() : undefined +>e.toUpperCase() : any +>e.toUpperCase : any +>e : unknown +>toUpperCase : any + + void e++; +>void e++ : undefined +>e++ : number +>e : unknown + + void e(); +>void e() : undefined +>e() : any +>e : unknown + + if (typeof e === "string") { +>typeof e === "string" : boolean +>typeof e : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>e : unknown +>"string" : "string" + + // works! + // We've narrowed 'e' down to the type 'string'. + console.log(e.toUpperCase()); +>console.log(e.toUpperCase()) : void +>console.log : (...data: any[]) => void +>console : Console +>log : (...data: any[]) => void +>e.toUpperCase() : string +>e.toUpperCase : () => string +>e : string +>toUpperCase : () => string + } + if (e instanceof Error) { +>e instanceof Error : boolean +>e : unknown +>Error : ErrorConstructor + + e.stack?.toUpperCase(); +>e.stack?.toUpperCase() : string +>e.stack?.toUpperCase : () => string +>e.stack : string +>e : Error +>stack : string +>toUpperCase : () => string + } + if (typeof e === "number") { +>typeof e === "number" : boolean +>typeof e : "string" | "number" | "bigint" | "boolean" | "symbol" | "undefined" | "object" | "function" +>e : unknown +>"number" : "number" + + e++; +>e++ : number +>e : number + } +} From cc6e761865bdb85dfd4f7d7b3fe6f395205a4af0 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 2 Dec 2020 13:16:39 -0800 Subject: [PATCH 4/8] Add test for catch variable explicitly typed as 'any'. --- tests/cases/compiler/useUnknownInCatchVariables01.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/cases/compiler/useUnknownInCatchVariables01.ts b/tests/cases/compiler/useUnknownInCatchVariables01.ts index c9633c97e4fcc..ca9bdda4acbd0 100644 --- a/tests/cases/compiler/useUnknownInCatchVariables01.ts +++ b/tests/cases/compiler/useUnknownInCatchVariables01.ts @@ -18,6 +18,18 @@ catch (e) { e.stack?.toUpperCase(); } if (typeof e === "number") { + e.toExponential(); e++; } +} + + +try { + // ... +} +catch (e: any) { + // All are allowed. + void e.toUpperCase(); + void e.toExponential(); + void e(); } \ No newline at end of file From cd08b62641862907acb69a7b3232f173ef64bcb0 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 2 Dec 2020 13:18:34 -0800 Subject: [PATCH 5/8] Accepted baselines. --- .../useUnknownInCatchVariables01.errors.txt | 12 +++++++ .../reference/useUnknownInCatchVariables01.js | 22 ++++++++++++ .../useUnknownInCatchVariables01.symbols | 23 +++++++++++++ .../useUnknownInCatchVariables01.types | 34 +++++++++++++++++++ 4 files changed, 91 insertions(+) diff --git a/tests/baselines/reference/useUnknownInCatchVariables01.errors.txt b/tests/baselines/reference/useUnknownInCatchVariables01.errors.txt index 21f7d8ba35499..017c3ada88cd0 100644 --- a/tests/baselines/reference/useUnknownInCatchVariables01.errors.txt +++ b/tests/baselines/reference/useUnknownInCatchVariables01.errors.txt @@ -30,6 +30,18 @@ tests/cases/compiler/useUnknownInCatchVariables01.ts(8,10): error TS2349: This e e.stack?.toUpperCase(); } if (typeof e === "number") { + e.toExponential(); e++; } + } + + + try { + // ... + } + catch (e: any) { + // All are allowed. + void e.toUpperCase(); + void e.toExponential(); + void e(); } \ No newline at end of file diff --git a/tests/baselines/reference/useUnknownInCatchVariables01.js b/tests/baselines/reference/useUnknownInCatchVariables01.js index 85dc03e4fb033..1e3a867d11855 100644 --- a/tests/baselines/reference/useUnknownInCatchVariables01.js +++ b/tests/baselines/reference/useUnknownInCatchVariables01.js @@ -17,8 +17,20 @@ catch (e) { e.stack?.toUpperCase(); } if (typeof e === "number") { + e.toExponential(); e++; } +} + + +try { + // ... +} +catch (e: any) { + // All are allowed. + void e.toUpperCase(); + void e.toExponential(); + void e(); } //// [useUnknownInCatchVariables01.js] @@ -40,6 +52,16 @@ catch (e) { (_a = e.stack) === null || _a === void 0 ? void 0 : _a.toUpperCase(); } if (typeof e === "number") { + e.toExponential(); e++; } } +try { + // ... +} +catch (e) { + // All are allowed. + void e.toUpperCase(); + void e.toExponential(); + void e(); +} diff --git a/tests/baselines/reference/useUnknownInCatchVariables01.symbols b/tests/baselines/reference/useUnknownInCatchVariables01.symbols index 0511ec3daac7d..f2261f8c36fbf 100644 --- a/tests/baselines/reference/useUnknownInCatchVariables01.symbols +++ b/tests/baselines/reference/useUnknownInCatchVariables01.symbols @@ -42,7 +42,30 @@ catch (e) { if (typeof e === "number") { >e : Symbol(e, Decl(useUnknownInCatchVariables01.ts, 3, 7)) + e.toExponential(); +>e.toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) +>e : Symbol(e, Decl(useUnknownInCatchVariables01.ts, 3, 7)) +>toExponential : Symbol(Number.toExponential, Decl(lib.es5.d.ts, --, --)) + e++; >e : Symbol(e, Decl(useUnknownInCatchVariables01.ts, 3, 7)) } } + + +try { + // ... +} +catch (e: any) { +>e : Symbol(e, Decl(useUnknownInCatchVariables01.ts, 27, 7)) + + // All are allowed. + void e.toUpperCase(); +>e : Symbol(e, Decl(useUnknownInCatchVariables01.ts, 27, 7)) + + void e.toExponential(); +>e : Symbol(e, Decl(useUnknownInCatchVariables01.ts, 27, 7)) + + void e(); +>e : Symbol(e, Decl(useUnknownInCatchVariables01.ts, 27, 7)) +} diff --git a/tests/baselines/reference/useUnknownInCatchVariables01.types b/tests/baselines/reference/useUnknownInCatchVariables01.types index f818272aefaa4..71a25687af0c2 100644 --- a/tests/baselines/reference/useUnknownInCatchVariables01.types +++ b/tests/baselines/reference/useUnknownInCatchVariables01.types @@ -60,8 +60,42 @@ catch (e) { >e : unknown >"number" : "number" + e.toExponential(); +>e.toExponential() : string +>e.toExponential : (fractionDigits?: number) => string +>e : number +>toExponential : (fractionDigits?: number) => string + e++; >e++ : number >e : number } } + + +try { + // ... +} +catch (e: any) { +>e : any + + // All are allowed. + void e.toUpperCase(); +>void e.toUpperCase() : undefined +>e.toUpperCase() : any +>e.toUpperCase : any +>e : any +>toUpperCase : any + + void e.toExponential(); +>void e.toExponential() : undefined +>e.toExponential() : any +>e.toExponential : any +>e : any +>toExponential : any + + void e(); +>void e() : undefined +>e() : any +>e : any +} From f5294570c8e1f71c678055cb90c2e2b97c562d6c Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 5 May 2021 21:35:57 +0000 Subject: [PATCH 6/8] Move option under 'strict'. --- src/compiler/checker.ts | 3 ++- src/compiler/utilities.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 3afe6983c6607..b098c42b53446 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -354,6 +354,7 @@ namespace ts { const strictPropertyInitialization = getStrictOptionValue(compilerOptions, "strictPropertyInitialization"); const noImplicitAny = getStrictOptionValue(compilerOptions, "noImplicitAny"); const noImplicitThis = getStrictOptionValue(compilerOptions, "noImplicitThis"); + const useUnknownInCatchVariables = getStrictOptionValue(compilerOptions, "useUnknownInCatchVariables"); const keyofStringsOnly = !!compilerOptions.keyofStringsOnly; const freshObjectLiteralFlag = compilerOptions.suppressExcessPropertyErrors ? 0 : ObjectFlags.FreshLiteral; @@ -8929,7 +8930,7 @@ namespace ts { if (isCatchClauseVariableDeclarationOrBindingElement(declaration)) { const typeNode = getEffectiveTypeAnnotationNode(declaration); if (typeNode === undefined) { - return compilerOptions.useUnknownInCatchVariables ? unknownType : anyType; + return useUnknownInCatchVariables ? unknownType : anyType; } const type = getTypeOfNode(typeNode); // an errorType will make `checkTryStatement` issue an error diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index 9bbdea8a6ac2d..88ba75d138247 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -6067,6 +6067,7 @@ namespace ts { | "strictBindCallApply" | "strictPropertyInitialization" | "alwaysStrict" + | "useUnknownInCatchVariables" ; export function getStrictOptionValue(compilerOptions: CompilerOptions, flag: StrictOptionName): boolean { From a32013b7ef4715f426dac74ca89eb6bfcf7c8335 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 5 May 2021 22:13:48 +0000 Subject: [PATCH 7/8] Accepted baselines. --- .../controlFlowForCatchAndFinally.types | 2 +- .../tryCatchFinallyControlFlow.types | 30 +++++++++---------- ...tatus.DiagnosticsPresent_OutputsSkipped.js | 1 + .../reference/typedefOnStatements.types | 2 +- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/tests/baselines/reference/controlFlowForCatchAndFinally.types b/tests/baselines/reference/controlFlowForCatchAndFinally.types index a041cac8ce09a..c159655a0a816 100644 --- a/tests/baselines/reference/controlFlowForCatchAndFinally.types +++ b/tests/baselines/reference/controlFlowForCatchAndFinally.types @@ -120,7 +120,7 @@ class Foo { >Aborter : typeof Aborter } catch (error) { ->error : any +>error : unknown if (this.abortController !== undefined) { >this.abortController !== undefined : boolean diff --git a/tests/baselines/reference/tryCatchFinallyControlFlow.types b/tests/baselines/reference/tryCatchFinallyControlFlow.types index 1674907b3a266..b5fb0cf95cc8c 100644 --- a/tests/baselines/reference/tryCatchFinallyControlFlow.types +++ b/tests/baselines/reference/tryCatchFinallyControlFlow.types @@ -19,10 +19,10 @@ function f1() { >a : number } catch (e) { ->e : any +>e : unknown throw e; ->e : any +>e : unknown } finally { if (a != null && a.toFixed(0) == "123") { @@ -55,7 +55,7 @@ function f2() { >1 : 1 } catch (e) { ->e : any +>e : unknown x = 2; >x = 2 : 2 @@ -63,7 +63,7 @@ function f2() { >2 : 2 throw e; ->e : any +>e : unknown } finally { x; // 0 | 1 | 2 @@ -87,7 +87,7 @@ function f3() { >1 : 1 } catch (e) { ->e : any +>e : unknown x = 2; >x = 2 : 2 @@ -118,7 +118,7 @@ function f4() { >1 : 1 } catch (e) { ->e : any +>e : unknown x = 2; >x = 2 : 2 @@ -149,7 +149,7 @@ function f5() { return; } catch (e) { ->e : any +>e : unknown x = 2; >x = 2 : 2 @@ -178,7 +178,7 @@ function f6() { >1 : 1 } catch (e) { ->e : any +>e : unknown x = 2; >x = 2 : 2 @@ -211,7 +211,7 @@ function f7() { return; } catch (e) { ->e : any +>e : unknown x = 2; >x = 2 : 2 @@ -324,7 +324,7 @@ function f10() { return; } catch (e) { ->e : any +>e : unknown x = 2; >x = 2 : 2 @@ -388,7 +388,7 @@ function f11() { } } catch (e) { ->e : any +>e : unknown x; // 0 | 1 | 2 >x : 0 | 1 | 2 @@ -466,7 +466,7 @@ function f12() { } } catch (e) { ->e : any +>e : unknown x; // 0 | 1 | 2 >x : 0 | 1 | 2 @@ -576,7 +576,7 @@ function t1() { >'x' : "x" } catch (e) { ->e : any +>e : unknown return null; >null : null @@ -626,7 +626,7 @@ function notallowed(arg: number) { finally { } } catch (err) { ->err : any +>err : unknown state.tag; >state.tag : "one" | "two" | "three" @@ -770,7 +770,7 @@ function f21() { >x : 3 | 4 | 5 } catch (e) { ->e : any +>e : unknown x; // 0 | 1 | 2 | 3 | 4 | 5 >x : 0 | 1 | 2 | 3 | 4 | 5 diff --git a/tests/baselines/reference/tsc/runWithoutArgs/initial-build/show-help-with-ExitStatus.DiagnosticsPresent_OutputsSkipped.js b/tests/baselines/reference/tsc/runWithoutArgs/initial-build/show-help-with-ExitStatus.DiagnosticsPresent_OutputsSkipped.js index 362d9209ea89e..774fd29297a9b 100644 --- a/tests/baselines/reference/tsc/runWithoutArgs/initial-build/show-help-with-ExitStatus.DiagnosticsPresent_OutputsSkipped.js +++ b/tests/baselines/reference/tsc/runWithoutArgs/initial-build/show-help-with-ExitStatus.DiagnosticsPresent_OutputsSkipped.js @@ -48,6 +48,7 @@ Options: --noUnusedParameters Report errors on unused parameters. --noImplicitReturns Report error when not all code paths in function return a value. --noFallthroughCasesInSwitch Report errors for fallthrough cases in switch statement. + --useUnknownInCatchVariables Type catch clause variables as 'unknown' instead of 'any'. --types Type declaration files to be included in compilation. --esModuleInterop Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. @ Insert command line options and files from a file. diff --git a/tests/baselines/reference/typedefOnStatements.types b/tests/baselines/reference/typedefOnStatements.types index 007a6a41d7658..c30a83a8afa81 100644 --- a/tests/baselines/reference/typedefOnStatements.types +++ b/tests/baselines/reference/typedefOnStatements.types @@ -68,7 +68,7 @@ throw new Error('Unreachable') try { } catch (e) { ->e : any +>e : unknown } /** From 0ea20ffb3a06dd1b270b0ceec11a54a18980fff1 Mon Sep 17 00:00:00 2001 From: Daniel Rosenwasser Date: Wed, 19 May 2021 07:47:33 +0000 Subject: [PATCH 8/8] 'useUnknownInCatchVariables' is strict in command line help. --- src/compiler/commandLineParser.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 819be6fadce94..390d3cb800ea8 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -692,6 +692,7 @@ namespace ts { name: "useUnknownInCatchVariables", type: "boolean", affectsSemanticDiagnostics: true, + strictFlag: true, showInSimplifiedHelpView: true, category: Diagnostics.Additional_Checks, description: Diagnostics.Type_catch_clause_variables_as_unknown_instead_of_any,