Skip to content

Commit f877b23

Browse files
committed
Treat catch clauses the same as other variable declarations
1 parent 165b112 commit f877b23

22 files changed

+327
-178
lines changed

src/compiler/checker.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10205,7 +10205,16 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1020510205
const isOptional = includeOptionality && isOptionalDeclaration(declaration);
1020610206

1020710207
// Use type from type annotation if one is present
10208-
const declaredType = tryGetTypeFromEffectiveTypeNode(declaration);
10208+
let declaredType = tryGetTypeFromEffectiveTypeNode(declaration);
10209+
if (isCatchClauseVariableDeclarationOrBindingElement(declaration)) {
10210+
if (declaredType) {
10211+
// If the catch clause is explicitly annotated with any or unknown, accept it, otherwise error.
10212+
return isTypeAny(declaredType) || declaredType === unknownType ? declaredType : errorType;
10213+
}
10214+
// If the catch clause is not explicitly annotated, treat it as though it were explicitly
10215+
// annotated with unknown or any, depending on useUnknownInCatchVariables.
10216+
declaredType = useUnknownInCatchVariables ? unknownType : anyType;
10217+
}
1020910218
if (declaredType) {
1021010219
return addOptionality(declaredType, isProperty, isOptional);
1021110220
}
@@ -10867,15 +10876,6 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
1086710876
// Handle catch clause variables
1086810877
Debug.assertIsDefined(symbol.valueDeclaration);
1086910878
const declaration = symbol.valueDeclaration;
10870-
if (isCatchClauseVariableDeclarationOrBindingElement(declaration)) {
10871-
const typeNode = getEffectiveTypeAnnotationNode(declaration);
10872-
if (typeNode === undefined) {
10873-
return useUnknownInCatchVariables ? unknownType : anyType;
10874-
}
10875-
const type = getTypeOfNode(typeNode);
10876-
// an errorType will make `checkTryStatement` issue an error
10877-
return isTypeAny(type) || type === unknownType ? type : errorType;
10878-
}
1087910879
// Handle export default expressions
1088010880
if (isSourceFile(declaration) && isJsonSourceFile(declaration)) {
1088110881
if (!declaration.statements.length) {
@@ -41178,9 +41178,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4117841178
// Grammar checking
4117941179
if (catchClause.variableDeclaration) {
4118041180
const declaration = catchClause.variableDeclaration;
41181-
const typeNode = getEffectiveTypeAnnotationNode(getRootDeclaration(declaration));
41181+
checkVariableLikeDeclaration(declaration);
41182+
const typeNode = getEffectiveTypeAnnotationNode(declaration);
4118241183
if (typeNode) {
41183-
const type = getTypeForVariableLikeDeclaration(declaration, /*includeOptionality*/ false, CheckMode.Normal);
41184+
const type = getTypeFromTypeNode(typeNode);
4118441185
if (type && !(type.flags & TypeFlags.AnyOrUnknown)) {
4118541186
grammarErrorOnFirstToken(typeNode, Diagnostics.Catch_clause_variable_type_annotation_must_be_any_or_unknown_if_specified);
4118641187
}

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 destructure
63+
~
64+
!!! error TS2339: Property 'x' does not exist on type '{}'.
65+
try { } catch ({ x }: unknown1) { console.log(x); } // error in the destructure
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 destructure
38+
try { } catch ({ x }: unknown1) { console.log(x); } // error in the destructure
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 destructure
128128
try { }
129129
catch (_e) {
130130
var x_6 = _e.x;
131131
console.log(x_6);
132-
} // should be OK
132+
} // error in the destructure
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 destructure
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 destructure
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 destructure
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 destructure
138138
>x : any
139139
>console.log(x) : void
140140
>console.log : (...data: any[]) => void
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
tests/cases/compiler/destructureCatchClause.ts(26,17): error TS2339: Property 'x' does not exist on type 'unknown'.
2+
tests/cases/compiler/destructureCatchClause.ts(27,15): error TS2461: Type 'unknown' is not an array type.
3+
tests/cases/compiler/destructureCatchClause.ts(29,17): error TS2339: Property 'a' does not exist on type 'unknown'.
4+
tests/cases/compiler/destructureCatchClause.ts(30,17): error TS2339: Property 'a' does not exist on type 'unknown'.
5+
tests/cases/compiler/destructureCatchClause.ts(32,15): error TS2461: Type 'unknown' is not an array type.
6+
tests/cases/compiler/destructureCatchClause.ts(33,15): error TS2461: Type 'unknown' is not an array type.
7+
tests/cases/compiler/destructureCatchClause.ts(35,17): error TS2339: Property 'a' does not exist on type 'unknown'.
8+
9+
10+
==== tests/cases/compiler/destructureCatchClause.ts (7 errors) ====
11+
// These are okay with useUnknownInCatchVariables=false, but not okay with useUnknownInCatchVariables=true.
12+
try {} catch ({ x }) { x }
13+
try {} catch ([ x ]) { x }
14+
15+
try {} catch ({ a: { x } }) { x }
16+
try {} catch ({ a: [ x ] }) { x }
17+
18+
try {} catch ([{ x }]) { x }
19+
try {} catch ([[ x ]]) { x }
20+
21+
try {} catch ({ a: { b: { c: { x }} }}) { x }
22+
23+
24+
try {} catch ({ x }: any) { x }
25+
try {} catch ([ x ]: any) { x }
26+
27+
try {} catch ({ a: { x } }: any) { x }
28+
try {} catch ({ a: [ x ] }: any) { x }
29+
30+
try {} catch ([{ x }]: any) { x }
31+
try {} catch ([[ x ]]: any) { x }
32+
33+
try {} catch ({ a: { b: { c: { x }} }}: any) { x }
34+
35+
36+
try {} catch ({ x }: unknown) { x }
37+
~
38+
!!! error TS2339: Property 'x' does not exist on type 'unknown'.
39+
try {} catch ([ x ]: unknown) { x }
40+
~~~~~
41+
!!! error TS2461: Type 'unknown' is not an array type.
42+
43+
try {} catch ({ a: { x } }: unknown) { x }
44+
~
45+
!!! error TS2339: Property 'a' does not exist on type 'unknown'.
46+
try {} catch ({ a: [ x ] }: unknown) { x }
47+
~
48+
!!! error TS2339: Property 'a' does not exist on type 'unknown'.
49+
50+
try {} catch ([{ x }]: unknown) { x }
51+
~~~~~~~
52+
!!! error TS2461: Type 'unknown' is not an array type.
53+
try {} catch ([[ x ]]: unknown) { x }
54+
~~~~~~~
55+
!!! error TS2461: Type 'unknown' is not an array type.
56+
57+
try {} catch ({ a: { b: { c: { x }} }}: unknown) { x }
58+
~
59+
!!! error TS2339: Property 'a' does not exist on type 'unknown'.
60+

tests/baselines/reference/destructureCatchClause(useunknownincatchvariables=false).js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//// [destructureCatchClause.ts]
2+
// These are okay with useUnknownInCatchVariables=false, but not okay with useUnknownInCatchVariables=true.
23
try {} catch ({ x }) { x }
34
try {} catch ([ x ]) { x }
45

@@ -37,6 +38,7 @@ try {} catch ({ a: { b: { c: { x }} }}: unknown) { x }
3738

3839
//// [destructureCatchClause.js]
3940
"use strict";
41+
// These are okay with useUnknownInCatchVariables=false, but not okay with useUnknownInCatchVariables=true.
4042
try { }
4143
catch (_a) {
4244
var x = _a.x;
Lines changed: 43 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,88 @@
11
=== tests/cases/compiler/destructureCatchClause.ts ===
2+
// These are okay with useUnknownInCatchVariables=false, but not okay with useUnknownInCatchVariables=true.
23
try {} catch ({ x }) { x }
3-
>x : Symbol(x, Decl(destructureCatchClause.ts, 0, 15))
4-
>x : Symbol(x, Decl(destructureCatchClause.ts, 0, 15))
5-
6-
try {} catch ([ x ]) { x }
74
>x : Symbol(x, Decl(destructureCatchClause.ts, 1, 15))
85
>x : Symbol(x, Decl(destructureCatchClause.ts, 1, 15))
96

10-
try {} catch ({ a: { x } }) { x }
11-
>a : Symbol(a)
12-
>x : Symbol(x, Decl(destructureCatchClause.ts, 3, 20))
13-
>x : Symbol(x, Decl(destructureCatchClause.ts, 3, 20))
7+
try {} catch ([ x ]) { x }
8+
>x : Symbol(x, Decl(destructureCatchClause.ts, 2, 15))
9+
>x : Symbol(x, Decl(destructureCatchClause.ts, 2, 15))
1410

15-
try {} catch ({ a: [ x ] }) { x }
16-
>a : Symbol(a)
11+
try {} catch ({ a: { x } }) { x }
1712
>x : Symbol(x, Decl(destructureCatchClause.ts, 4, 20))
1813
>x : Symbol(x, Decl(destructureCatchClause.ts, 4, 20))
1914

20-
try {} catch ([{ x }]) { x }
21-
>x : Symbol(x, Decl(destructureCatchClause.ts, 6, 16))
22-
>x : Symbol(x, Decl(destructureCatchClause.ts, 6, 16))
15+
try {} catch ({ a: [ x ] }) { x }
16+
>x : Symbol(x, Decl(destructureCatchClause.ts, 5, 20))
17+
>x : Symbol(x, Decl(destructureCatchClause.ts, 5, 20))
2318

24-
try {} catch ([[ x ]]) { x }
19+
try {} catch ([{ x }]) { x }
2520
>x : Symbol(x, Decl(destructureCatchClause.ts, 7, 16))
2621
>x : Symbol(x, Decl(destructureCatchClause.ts, 7, 16))
2722

23+
try {} catch ([[ x ]]) { x }
24+
>x : Symbol(x, Decl(destructureCatchClause.ts, 8, 16))
25+
>x : Symbol(x, Decl(destructureCatchClause.ts, 8, 16))
26+
2827
try {} catch ({ a: { b: { c: { x }} }}) { x }
29-
>a : Symbol(a)
30-
>b : Symbol(b)
31-
>c : Symbol(c)
32-
>x : Symbol(x, Decl(destructureCatchClause.ts, 9, 30))
33-
>x : Symbol(x, Decl(destructureCatchClause.ts, 9, 30))
28+
>x : Symbol(x, Decl(destructureCatchClause.ts, 10, 30))
29+
>x : Symbol(x, Decl(destructureCatchClause.ts, 10, 30))
3430

3531

3632
try {} catch ({ x }: any) { x }
37-
>x : Symbol(x, Decl(destructureCatchClause.ts, 12, 15))
38-
>x : Symbol(x, Decl(destructureCatchClause.ts, 12, 15))
39-
40-
try {} catch ([ x ]: any) { x }
4133
>x : Symbol(x, Decl(destructureCatchClause.ts, 13, 15))
4234
>x : Symbol(x, Decl(destructureCatchClause.ts, 13, 15))
4335

44-
try {} catch ({ a: { x } }: any) { x }
45-
>x : Symbol(x, Decl(destructureCatchClause.ts, 15, 20))
46-
>x : Symbol(x, Decl(destructureCatchClause.ts, 15, 20))
36+
try {} catch ([ x ]: any) { x }
37+
>x : Symbol(x, Decl(destructureCatchClause.ts, 14, 15))
38+
>x : Symbol(x, Decl(destructureCatchClause.ts, 14, 15))
4739

48-
try {} catch ({ a: [ x ] }: any) { x }
40+
try {} catch ({ a: { x } }: any) { x }
4941
>x : Symbol(x, Decl(destructureCatchClause.ts, 16, 20))
5042
>x : Symbol(x, Decl(destructureCatchClause.ts, 16, 20))
5143

52-
try {} catch ([{ x }]: any) { x }
53-
>x : Symbol(x, Decl(destructureCatchClause.ts, 18, 16))
54-
>x : Symbol(x, Decl(destructureCatchClause.ts, 18, 16))
44+
try {} catch ({ a: [ x ] }: any) { x }
45+
>x : Symbol(x, Decl(destructureCatchClause.ts, 17, 20))
46+
>x : Symbol(x, Decl(destructureCatchClause.ts, 17, 20))
5547

56-
try {} catch ([[ x ]]: any) { x }
48+
try {} catch ([{ x }]: any) { x }
5749
>x : Symbol(x, Decl(destructureCatchClause.ts, 19, 16))
5850
>x : Symbol(x, Decl(destructureCatchClause.ts, 19, 16))
5951

52+
try {} catch ([[ x ]]: any) { x }
53+
>x : Symbol(x, Decl(destructureCatchClause.ts, 20, 16))
54+
>x : Symbol(x, Decl(destructureCatchClause.ts, 20, 16))
55+
6056
try {} catch ({ a: { b: { c: { x }} }}: any) { x }
61-
>x : Symbol(x, Decl(destructureCatchClause.ts, 21, 30))
62-
>x : Symbol(x, Decl(destructureCatchClause.ts, 21, 30))
57+
>x : Symbol(x, Decl(destructureCatchClause.ts, 22, 30))
58+
>x : Symbol(x, Decl(destructureCatchClause.ts, 22, 30))
6359

6460

6561
try {} catch ({ x }: unknown) { x }
66-
>x : Symbol(x, Decl(destructureCatchClause.ts, 24, 15))
67-
>x : Symbol(x, Decl(destructureCatchClause.ts, 24, 15))
68-
69-
try {} catch ([ x ]: unknown) { x }
7062
>x : Symbol(x, Decl(destructureCatchClause.ts, 25, 15))
7163
>x : Symbol(x, Decl(destructureCatchClause.ts, 25, 15))
7264

73-
try {} catch ({ a: { x } }: unknown) { x }
74-
>x : Symbol(x, Decl(destructureCatchClause.ts, 27, 20))
75-
>x : Symbol(x, Decl(destructureCatchClause.ts, 27, 20))
65+
try {} catch ([ x ]: unknown) { x }
66+
>x : Symbol(x, Decl(destructureCatchClause.ts, 26, 15))
67+
>x : Symbol(x, Decl(destructureCatchClause.ts, 26, 15))
7668

77-
try {} catch ({ a: [ x ] }: unknown) { x }
69+
try {} catch ({ a: { x } }: unknown) { x }
7870
>x : Symbol(x, Decl(destructureCatchClause.ts, 28, 20))
7971
>x : Symbol(x, Decl(destructureCatchClause.ts, 28, 20))
8072

81-
try {} catch ([{ x }]: unknown) { x }
82-
>x : Symbol(x, Decl(destructureCatchClause.ts, 30, 16))
83-
>x : Symbol(x, Decl(destructureCatchClause.ts, 30, 16))
73+
try {} catch ({ a: [ x ] }: unknown) { x }
74+
>x : Symbol(x, Decl(destructureCatchClause.ts, 29, 20))
75+
>x : Symbol(x, Decl(destructureCatchClause.ts, 29, 20))
8476

85-
try {} catch ([[ x ]]: unknown) { x }
77+
try {} catch ([{ x }]: unknown) { x }
8678
>x : Symbol(x, Decl(destructureCatchClause.ts, 31, 16))
8779
>x : Symbol(x, Decl(destructureCatchClause.ts, 31, 16))
8880

81+
try {} catch ([[ x ]]: unknown) { x }
82+
>x : Symbol(x, Decl(destructureCatchClause.ts, 32, 16))
83+
>x : Symbol(x, Decl(destructureCatchClause.ts, 32, 16))
84+
8985
try {} catch ({ a: { b: { c: { x }} }}: unknown) { x }
90-
>x : Symbol(x, Decl(destructureCatchClause.ts, 33, 30))
91-
>x : Symbol(x, Decl(destructureCatchClause.ts, 33, 30))
86+
>x : Symbol(x, Decl(destructureCatchClause.ts, 34, 30))
87+
>x : Symbol(x, Decl(destructureCatchClause.ts, 34, 30))
9288

tests/baselines/reference/destructureCatchClause(useunknownincatchvariables=false).types

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
=== tests/cases/compiler/destructureCatchClause.ts ===
2+
// These are okay with useUnknownInCatchVariables=false, but not okay with useUnknownInCatchVariables=true.
23
try {} catch ({ x }) { x }
34
>x : any
45
>x : any

0 commit comments

Comments
 (0)