Skip to content

Commit 5599944

Browse files
committed
fix(47383): throw an error on catch variable with unknown type
1 parent 935c05c commit 5599944

File tree

30 files changed

+739
-40
lines changed

30 files changed

+739
-40
lines changed

src/compiler/checker.ts

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8665,6 +8665,16 @@ namespace ts {
86658665
return undefined;
86668666
}
86678667

8668+
function getTypeForCatchClauseVariableDeclaration(declaration: VariableDeclaration) {
8669+
const typeNode = getEffectiveTypeAnnotationNode(declaration);
8670+
if (typeNode === undefined) {
8671+
return useUnknownInCatchVariables ? unknownType : anyType;
8672+
}
8673+
const type = getTypeOfNode(typeNode);
8674+
// an errorType will make `checkTryStatement` issue an error
8675+
return isTypeAny(type) || type === unknownType ? type : errorType;
8676+
}
8677+
86688678
function isNullOrUndefined(node: Expression) {
86698679
const expr = skipParentheses(node, /*excludeJSDocTypeAssertions*/ true);
86708680
return expr.kind === SyntaxKind.NullKeyword || expr.kind === SyntaxKind.Identifier && getResolvedSymbol(expr as Identifier) === undefinedSymbol;
@@ -8697,6 +8707,10 @@ namespace ts {
86978707
return checkRightHandSideOfForOf(forOfStatement) || anyType;
86988708
}
86998709

8710+
if (isCatchClauseVariableDeclaration(declaration)) {
8711+
return getTypeForCatchClauseVariableDeclaration(declaration as VariableDeclaration);
8712+
}
8713+
87008714
if (isBindingPattern(declaration.parent)) {
87018715
return getTypeForBindingElement(declaration as BindingElement);
87028716
}
@@ -9345,15 +9359,6 @@ namespace ts {
93459359
// Handle catch clause variables
93469360
Debug.assertIsDefined(symbol.valueDeclaration);
93479361
const declaration = symbol.valueDeclaration;
9348-
if (isCatchClauseVariableDeclarationOrBindingElement(declaration)) {
9349-
const typeNode = getEffectiveTypeAnnotationNode(declaration);
9350-
if (typeNode === undefined) {
9351-
return useUnknownInCatchVariables ? unknownType : anyType;
9352-
}
9353-
const type = getTypeOfNode(typeNode);
9354-
// an errorType will make `checkTryStatement` issue an error
9355-
return isTypeAny(type) || type === unknownType ? type : errorType;
9356-
}
93579362
// Handle export default expressions
93589363
if (isSourceFile(declaration) && isJsonSourceFile(declaration)) {
93599364
if (!declaration.statements.length) {
@@ -38315,17 +38320,20 @@ namespace ts {
3831538320
function checkTryStatement(node: TryStatement) {
3831638321
// Grammar checking
3831738322
checkGrammarStatementInAmbientContext(node);
38318-
3831938323
checkBlock(node.tryBlock);
38324+
3832038325
const catchClause = node.catchClause;
3832138326
if (catchClause) {
3832238327
// Grammar checking
3832338328
if (catchClause.variableDeclaration) {
3832438329
const declaration = catchClause.variableDeclaration;
38330+
if (isBindingPattern(declaration.name)) {
38331+
forEach(declaration.name.elements, checkSourceElement);
38332+
}
3832538333
const typeNode = getEffectiveTypeAnnotationNode(getRootDeclaration(declaration));
3832638334
if (typeNode) {
3832738335
const type = getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ false);
38328-
if (type && !(type.flags & TypeFlags.AnyOrUnknown)) {
38336+
if (type === errorType) {
3832938337
grammarErrorOnFirstToken(typeNode, Diagnostics.Catch_clause_variable_type_annotation_must_be_any_or_unknown_if_specified);
3833038338
}
3833138339
}

tests/baselines/reference/catchClauseWithTypeAnnotation.errors.txt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.t
44
tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts(20,23): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified.
55
tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts(29,29): error TS2492: Cannot redeclare identifier 'x' in catch clause.
66
tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts(30,29): error TS2403: Subsequent variable declarations must have the same type. Variable 'x' must be of type 'boolean', but here has type 'string'.
7+
tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts(36,22): error TS2339: Property 'x' does not exist on type '{}'.
8+
tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts(37,22): error TS2339: Property 'x' does not exist on type '{}'.
79
tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts(38,27): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified.
810
tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts(39,27): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified.
911

1012

11-
==== tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts (8 errors) ====
13+
==== tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.ts (10 errors) ====
1214
type any1 = any;
1315
type unknown1 = unknown;
1416

@@ -57,8 +59,12 @@ tests/cases/conformance/statements/tryStatements/catchClauseWithTypeAnnotation.t
5759
try { } catch ({ x }) { } // should be OK
5860
try { } catch ({ x }: any) { x.foo; } // should be OK
5961
try { } catch ({ x }: any1) { x.foo;} // should be OK
60-
try { } catch ({ x }: unknown) { console.log(x); } // should be OK
61-
try { } catch ({ x }: unknown1) { console.log(x); } // should be OK
62+
try { } catch ({ x }: unknown) { console.log(x); } // error in the type
63+
~
64+
!!! error TS2339: Property 'x' does not exist on type '{}'.
65+
try { } catch ({ x }: unknown1) { console.log(x); } // error in the type
66+
~
67+
!!! error TS2339: Property 'x' does not exist on type '{}'.
6268
try { } catch ({ x }: object) { } // error in the type
6369
~~~~~~
6470
!!! error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified.

tests/baselines/reference/catchClauseWithTypeAnnotation.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,8 @@ function fn(x: boolean) {
3434
try { } catch ({ x }) { } // should be OK
3535
try { } catch ({ x }: any) { x.foo; } // should be OK
3636
try { } catch ({ x }: any1) { x.foo;} // should be OK
37-
try { } catch ({ x }: unknown) { console.log(x); } // should be OK
38-
try { } catch ({ x }: unknown1) { console.log(x); } // should be OK
37+
try { } catch ({ x }: unknown) { console.log(x); } // error in the type
38+
try { } catch ({ x }: unknown1) { console.log(x); } // error in the type
3939
try { } catch ({ x }: object) { } // error in the type
4040
try { } catch ({ x }: Error) { } // error in the type
4141
}
@@ -124,12 +124,12 @@ function fn(x) {
124124
catch (_d) {
125125
var x_5 = _d.x;
126126
console.log(x_5);
127-
} // should be OK
127+
} // error in the type
128128
try { }
129129
catch (_e) {
130130
var x_6 = _e.x;
131131
console.log(x_6);
132-
} // should be OK
132+
} // error in the type
133133
try { }
134134
catch (_f) {
135135
var x_7 = _f.x;

tests/baselines/reference/catchClauseWithTypeAnnotation.symbols

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,14 @@ function fn(x: boolean) {
112112
>any1 : Symbol(any1, Decl(catchClauseWithTypeAnnotation.ts, 0, 0))
113113
>x : Symbol(x, Decl(catchClauseWithTypeAnnotation.ts, 34, 20))
114114

115-
try { } catch ({ x }: unknown) { console.log(x); } // should be OK
115+
try { } catch ({ x }: unknown) { console.log(x); } // error in the type
116116
>x : Symbol(x, Decl(catchClauseWithTypeAnnotation.ts, 35, 20))
117117
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
118118
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
119119
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
120120
>x : Symbol(x, Decl(catchClauseWithTypeAnnotation.ts, 35, 20))
121121

122-
try { } catch ({ x }: unknown1) { console.log(x); } // should be OK
122+
try { } catch ({ x }: unknown1) { console.log(x); } // error in the type
123123
>x : Symbol(x, Decl(catchClauseWithTypeAnnotation.ts, 36, 20))
124124
>unknown1 : Symbol(unknown1, Decl(catchClauseWithTypeAnnotation.ts, 0, 16))
125125
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))

tests/baselines/reference/catchClauseWithTypeAnnotation.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,15 +126,15 @@ function fn(x: boolean) {
126126
>x : any
127127
>foo : any
128128

129-
try { } catch ({ x }: unknown) { console.log(x); } // should be OK
129+
try { } catch ({ x }: unknown) { console.log(x); } // error in the type
130130
>x : any
131131
>console.log(x) : void
132132
>console.log : (...data: any[]) => void
133133
>console : Console
134134
>log : (...data: any[]) => void
135135
>x : any
136136

137-
try { } catch ({ x }: unknown1) { console.log(x); } // should be OK
137+
try { } catch ({ x }: unknown1) { console.log(x); } // error in the type
138138
>x : any
139139
>console.log(x) : void
140140
>console.log : (...data: any[]) => void

tests/baselines/reference/destructuringCatch.symbols

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ try {
3131
>z : Symbol(z, Decl(destructuringCatch.ts, 15, 20))
3232
}
3333
catch ([{x: [y], z}]) {
34-
>x : Symbol(x)
3534
>y : Symbol(y, Decl(destructuringCatch.ts, 17, 13))
3635
>z : Symbol(z, Decl(destructuringCatch.ts, 17, 16))
3736

tests/baselines/reference/jsdocCatchClauseWithTypeAnnotation.errors.txt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ tests/cases/conformance/jsdoc/foo.js(21,54): error TS2339: Property 'foo' does n
33
tests/cases/conformance/jsdoc/foo.js(22,31): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified.
44
tests/cases/conformance/jsdoc/foo.js(23,31): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified.
55
tests/cases/conformance/jsdoc/foo.js(35,7): error TS2492: Cannot redeclare identifier 'err' in catch clause.
6+
tests/cases/conformance/jsdoc/foo.js(46,45): error TS2339: Property 'x' does not exist on type '{}'.
7+
tests/cases/conformance/jsdoc/foo.js(47,45): error TS2339: Property 'x' does not exist on type '{}'.
68
tests/cases/conformance/jsdoc/foo.js(48,31): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified.
79
tests/cases/conformance/jsdoc/foo.js(49,31): error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified.
810

911

10-
==== tests/cases/conformance/jsdoc/foo.js (7 errors) ====
12+
==== tests/cases/conformance/jsdoc/foo.js (9 errors) ====
1113
/**
1214
* @typedef {any} Any
1315
*/
@@ -63,8 +65,12 @@ tests/cases/conformance/jsdoc/foo.js(49,31): error TS1196: Catch clause variable
6365
try { } catch ({ x }) { } // should be OK
6466
try { } catch (/** @type {any} */ { x }) { x.foo; } // should be OK
6567
try { } catch (/** @type {Any} */ { x }) { x.foo;} // should be OK
66-
try { } catch (/** @type {unknown} */ { x }) { console.log(x); } // should be OK
67-
try { } catch (/** @type {Unknown} */ { x }) { console.log(x); } // should be OK
68+
try { } catch (/** @type {unknown} */ { x }) { console.log(x); } // error in the type
69+
~
70+
!!! error TS2339: Property 'x' does not exist on type '{}'.
71+
try { } catch (/** @type {Unknown} */ { x }) { console.log(x); } // error in the type
72+
~
73+
!!! error TS2339: Property 'x' does not exist on type '{}'.
6874
try { } catch (/** @type {Error} */ { x }) { } // error in the type
6975
~~~~~
7076
!!! error TS1196: Catch clause variable type annotation must be 'any' or 'unknown' if specified.

tests/baselines/reference/jsdocCatchClauseWithTypeAnnotation.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ function fn() {
4444
try { } catch ({ x }) { } // should be OK
4545
try { } catch (/** @type {any} */ { x }) { x.foo; } // should be OK
4646
try { } catch (/** @type {Any} */ { x }) { x.foo;} // should be OK
47-
try { } catch (/** @type {unknown} */ { x }) { console.log(x); } // should be OK
48-
try { } catch (/** @type {Unknown} */ { x }) { console.log(x); } // should be OK
47+
try { } catch (/** @type {unknown} */ { x }) { console.log(x); } // error in the type
48+
try { } catch (/** @type {Unknown} */ { x }) { console.log(x); } // error in the type
4949
try { } catch (/** @type {Error} */ { x }) { } // error in the type
5050
try { } catch (/** @type {object} */ { x }) { } // error in the type
5151
}
@@ -132,11 +132,11 @@ function fn() {
132132
try { }
133133
catch ( /** @type {unknown} */{ x }) {
134134
console.log(x);
135-
} // should be OK
135+
} // error in the type
136136
try { }
137137
catch ( /** @type {Unknown} */{ x }) {
138138
console.log(x);
139-
} // should be OK
139+
} // error in the type
140140
try { }
141141
catch ( /** @type {Error} */{ x }) { } // error in the type
142142
try { }

tests/baselines/reference/jsdocCatchClauseWithTypeAnnotation.symbols

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,14 +110,14 @@ function fn() {
110110
>x : Symbol(x, Decl(foo.js, 44, 39))
111111
>x : Symbol(x, Decl(foo.js, 44, 39))
112112

113-
try { } catch (/** @type {unknown} */ { x }) { console.log(x); } // should be OK
113+
try { } catch (/** @type {unknown} */ { x }) { console.log(x); } // error in the type
114114
>x : Symbol(x, Decl(foo.js, 45, 43))
115115
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
116116
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))
117117
>log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
118118
>x : Symbol(x, Decl(foo.js, 45, 43))
119119

120-
try { } catch (/** @type {Unknown} */ { x }) { console.log(x); } // should be OK
120+
try { } catch (/** @type {Unknown} */ { x }) { console.log(x); } // error in the type
121121
>x : Symbol(x, Decl(foo.js, 46, 43))
122122
>console.log : Symbol(Console.log, Decl(lib.dom.d.ts, --, --))
123123
>console : Symbol(console, Decl(lib.dom.d.ts, --, --))

tests/baselines/reference/jsdocCatchClauseWithTypeAnnotation.types

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,15 +131,15 @@ function fn() {
131131
>x : any
132132
>foo : any
133133

134-
try { } catch (/** @type {unknown} */ { x }) { console.log(x); } // should be OK
134+
try { } catch (/** @type {unknown} */ { x }) { console.log(x); } // error in the type
135135
>x : any
136136
>console.log(x) : void
137137
>console.log : (...data: any[]) => void
138138
>console : Console
139139
>log : (...data: any[]) => void
140140
>x : any
141141

142-
try { } catch (/** @type {Unknown} */ { x }) { console.log(x); } // should be OK
142+
try { } catch (/** @type {Unknown} */ { x }) { console.log(x); } // error in the type
143143
>x : any
144144
>console.log(x) : void
145145
>console.log : (...data: any[]) => void

tests/baselines/reference/redeclareParameterInCatchBlock.symbols

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,7 @@ try {
3131
try {
3232

3333
} catch ({ a: x, b: x }) {
34-
>a : Symbol(a)
3534
>x : Symbol(x, Decl(redeclareParameterInCatchBlock.ts, 20, 10))
36-
>b : Symbol(b)
3735
>x : Symbol(x, Decl(redeclareParameterInCatchBlock.ts, 20, 16))
3836

3937
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
tests/cases/compiler/useUnknownInCatchVariables02.ts(11,10): error TS2339: Property 'name' does not exist on type '{}'.
2+
tests/cases/compiler/useUnknownInCatchVariables02.ts(24,10): error TS2339: Property 'name' does not exist on type '{}'.
3+
4+
5+
==== tests/cases/compiler/useUnknownInCatchVariables02.ts (2 errors) ====
6+
try {
7+
// ...
8+
}
9+
catch ({ name }) {
10+
name;
11+
}
12+
13+
try {
14+
// ...
15+
}
16+
catch ({ name }: unknown) {
17+
~~~~
18+
!!! error TS2339: Property 'name' does not exist on type '{}'.
19+
name;
20+
}
21+
22+
try {
23+
// ...
24+
}
25+
catch ({ name }) {
26+
}
27+
28+
try {
29+
// ...
30+
}
31+
catch ({ name }: unknown) {
32+
~~~~
33+
!!! error TS2339: Property 'name' does not exist on type '{}'.
34+
}
35+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
//// [useUnknownInCatchVariables02.ts]
2+
try {
3+
// ...
4+
}
5+
catch ({ name }) {
6+
name;
7+
}
8+
9+
try {
10+
// ...
11+
}
12+
catch ({ name }: unknown) {
13+
name;
14+
}
15+
16+
try {
17+
// ...
18+
}
19+
catch ({ name }) {
20+
}
21+
22+
try {
23+
// ...
24+
}
25+
catch ({ name }: unknown) {
26+
}
27+
28+
29+
//// [useUnknownInCatchVariables02.js]
30+
try {
31+
// ...
32+
}
33+
catch (_a) {
34+
var name = _a.name;
35+
name;
36+
}
37+
try {
38+
// ...
39+
}
40+
catch (_b) {
41+
var name = _b.name;
42+
name;
43+
}
44+
try {
45+
// ...
46+
}
47+
catch (_c) {
48+
var name = _c.name;
49+
}
50+
try {
51+
// ...
52+
}
53+
catch (_d) {
54+
var name = _d.name;
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
=== tests/cases/compiler/useUnknownInCatchVariables02.ts ===
2+
try {
3+
// ...
4+
}
5+
catch ({ name }) {
6+
>name : Symbol(name, Decl(useUnknownInCatchVariables02.ts, 3, 8))
7+
8+
name;
9+
>name : Symbol(name, Decl(useUnknownInCatchVariables02.ts, 3, 8))
10+
}
11+
12+
try {
13+
// ...
14+
}
15+
catch ({ name }: unknown) {
16+
>name : Symbol(name, Decl(useUnknownInCatchVariables02.ts, 10, 8))
17+
18+
name;
19+
>name : Symbol(name, Decl(useUnknownInCatchVariables02.ts, 10, 8))
20+
}
21+
22+
try {
23+
// ...
24+
}
25+
catch ({ name }) {
26+
>name : Symbol(name, Decl(useUnknownInCatchVariables02.ts, 17, 8))
27+
}
28+
29+
try {
30+
// ...
31+
}
32+
catch ({ name }: unknown) {
33+
>name : Symbol(name, Decl(useUnknownInCatchVariables02.ts, 23, 8))
34+
}
35+

0 commit comments

Comments
 (0)