Skip to content

Commit 1bc621b

Browse files
authored
Add Try statement to typescript (#349)
1 parent 97d4f94 commit 1bc621b

File tree

6 files changed

+282
-3
lines changed

6 files changed

+282
-3
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
changeKind: feature
3+
packages:
4+
- "@alloy-js/typescript"
5+
---
6+
7+
Add TryStatement, CatchClause, and FinallyClause components.
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
changeKind: fix
3+
packages:
4+
- "@alloy-js/typescript"
5+
---
6+
7+
IfElseClause and ElseClause properly create block scopes.

packages/typescript/src/components/IfStatement.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Block, type Children } from "@alloy-js/core";
1+
import { type Children } from "@alloy-js/core";
22
import { BlockScope } from "./BlockScope.jsx";
33

44
export interface IfStatementProps {
@@ -29,7 +29,7 @@ export function ElseIfClause(props: ElseIfClauseProps) {
2929
return (
3030
<>
3131
{" "}
32-
else if ({props.condition}) <Block>{props.children}</Block>
32+
else if ({props.condition}) <BlockScope>{props.children}</BlockScope>
3333
</>
3434
);
3535
}
@@ -45,7 +45,7 @@ export function ElseClause(props: ElseClauseProps) {
4545
return (
4646
<>
4747
{" "}
48-
else <Block>{props.children}</Block>
48+
else <BlockScope>{props.children}</BlockScope>
4949
</>
5050
);
5151
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
import { refkey, StatementList } from "@alloy-js/core";
2+
import { d } from "@alloy-js/core/testing";
3+
import { expect, it } from "vitest";
4+
import { toSourceText } from "../../test/utils.jsx";
5+
import { CatchClause, FinallyClause, TryStatement } from "./TryStatement.jsx";
6+
import { VarDeclaration } from "./VarDeclaration.jsx";
7+
8+
it("works with try-catch", () => {
9+
const text = toSourceText(
10+
<>
11+
<TryStatement>// try something</TryStatement>
12+
<CatchClause parameter="error">// handle error</CatchClause>
13+
</>,
14+
);
15+
16+
expect(text).toBe(d`
17+
try {
18+
// try something
19+
} catch (error) {
20+
// handle error
21+
}
22+
`);
23+
});
24+
25+
it("works with try-catch-finally", () => {
26+
const text = toSourceText(
27+
<>
28+
<TryStatement>// try something</TryStatement>
29+
<CatchClause parameter="error">// handle error</CatchClause>
30+
<FinallyClause>// cleanup</FinallyClause>
31+
</>,
32+
);
33+
34+
expect(text).toBe(d`
35+
try {
36+
// try something
37+
} catch (error) {
38+
// handle error
39+
} finally {
40+
// cleanup
41+
}
42+
`);
43+
});
44+
45+
it("works with try-finally", () => {
46+
const text = toSourceText(
47+
<>
48+
<TryStatement>// try something</TryStatement>
49+
<FinallyClause>// cleanup</FinallyClause>
50+
</>,
51+
);
52+
53+
expect(text).toBe(d`
54+
try {
55+
// try something
56+
} finally {
57+
// cleanup
58+
}
59+
`);
60+
});
61+
62+
it("works with catch without parameter", () => {
63+
const text = toSourceText(
64+
<>
65+
<TryStatement>// try something</TryStatement>
66+
<CatchClause>// handle error without parameter</CatchClause>
67+
</>,
68+
);
69+
70+
expect(text).toBe(d`
71+
try {
72+
// try something
73+
} catch {
74+
// handle error without parameter
75+
}
76+
`);
77+
});
78+
79+
it("works with typed catch parameter", () => {
80+
const text = toSourceText(
81+
<>
82+
<TryStatement>// try something</TryStatement>
83+
<CatchClause parameter={{ name: "error", type: "Error" }}>
84+
// handle typed error
85+
</CatchClause>
86+
</>,
87+
);
88+
89+
expect(text).toBe(d`
90+
try {
91+
// try something
92+
} catch (error: Error) {
93+
// handle typed error
94+
}
95+
`);
96+
});
97+
98+
it("creates symbols for catch parameters", () => {
99+
const rk = refkey();
100+
101+
const text = toSourceText(
102+
<>
103+
<TryStatement>// try something</TryStatement>
104+
<CatchClause parameter={{ name: "error", type: "Error", refkey: rk }}>
105+
<StatementList>
106+
{rk}
107+
<VarDeclaration name="message" type="string">
108+
{rk}.message
109+
</VarDeclaration>
110+
</StatementList>
111+
</CatchClause>
112+
</>,
113+
);
114+
115+
expect(text).toBe(d`
116+
try {
117+
// try something
118+
} catch (error: Error) {
119+
error;
120+
const message: string = error.message;
121+
}
122+
`);
123+
});
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
import { onCleanup, type Children } from "@alloy-js/core";
2+
import { useTSNamePolicy } from "../name-policy.js";
3+
import type { ParameterDescriptor } from "../parameter-descriptor.js";
4+
import { createValueSymbol, TSSymbolFlags } from "../symbols/index.js";
5+
import { BlockScope } from "./BlockScope.jsx";
6+
7+
export interface TryStatementProps {
8+
children: Children;
9+
}
10+
11+
/**
12+
* A try statement.
13+
*
14+
* @example
15+
* ```tsx
16+
* <TryStatement>
17+
* // risky operation
18+
* </TryStatement>
19+
* <CatchClause parameter="error">
20+
* console.error(error);
21+
* </CatchClause>
22+
* ```
23+
* renders to
24+
* ```ts
25+
* try {
26+
* // risky operation
27+
* } catch (error) {
28+
* console.error(error);
29+
* }
30+
* ```
31+
*/
32+
export function TryStatement(props: TryStatementProps) {
33+
return (
34+
<>
35+
try <BlockScope>{props.children}</BlockScope>
36+
</>
37+
);
38+
}
39+
40+
export interface CatchClauseProps {
41+
parameter?: ParameterDescriptor | string;
42+
children: Children;
43+
}
44+
45+
/**
46+
* A catch clause of a try statement.
47+
*
48+
* @example
49+
* ```tsx
50+
* <CatchClause parameter={{ name: "error", type: "unknown" }}>
51+
* console.error(error);
52+
* </CatchClause>
53+
* ```
54+
* renders to
55+
* ```ts
56+
* catch (error: unknown) {
57+
* console.error(error);
58+
* }
59+
* ```
60+
*
61+
* @remarks
62+
* The parameter can be omitted for a catch-all clause, or provided as a string
63+
* or {@link ParameterDescriptor}. When using a descriptor, a refkey can be provided
64+
* to reference the parameter within the catch block. Note that TypeScript only allows
65+
* `any` or `unknown` as catch parameter types.
66+
*/
67+
export function CatchClause(props: CatchClauseProps) {
68+
if (!props.parameter) {
69+
return (
70+
<>
71+
{" "}
72+
catch <BlockScope>{props.children}</BlockScope>
73+
</>
74+
);
75+
}
76+
77+
const namePolicy = useTSNamePolicy();
78+
const paramDesc =
79+
typeof props.parameter === "string" ?
80+
{ name: props.parameter }
81+
: props.parameter;
82+
83+
const symbol = createValueSymbol(paramDesc.name, {
84+
refkeys: paramDesc.refkey,
85+
tsFlags: TSSymbolFlags.ParameterSymbol,
86+
metadata: paramDesc.metadata,
87+
namePolicy: namePolicy.for("parameter"),
88+
});
89+
90+
onCleanup(() => {
91+
symbol.delete();
92+
});
93+
94+
const param = (
95+
<>
96+
{symbol.name}
97+
{paramDesc.type && <>: {paramDesc.type}</>}
98+
</>
99+
);
100+
101+
return (
102+
<>
103+
{" "}
104+
catch ({param}) <BlockScope>{props.children}</BlockScope>
105+
</>
106+
);
107+
}
108+
109+
export interface FinallyClauseProps {
110+
children: Children;
111+
}
112+
113+
/**
114+
* A finally clause of a try statement.
115+
*
116+
* @example
117+
* ```tsx
118+
* <TryStatement>
119+
* // risky operation
120+
* </TryStatement>
121+
* <FinallyClause>
122+
* // cleanup
123+
* </FinallyClause>
124+
* ```
125+
* renders to
126+
* ```ts
127+
* try {
128+
* // risky operation
129+
* } finally {
130+
* // cleanup
131+
* }
132+
* ```
133+
*/
134+
export function FinallyClause(props: FinallyClauseProps) {
135+
return (
136+
<>
137+
{" "}
138+
finally <BlockScope>{props.children}</BlockScope>
139+
</>
140+
);
141+
}

packages/typescript/src/components/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export * from "./Reference.js";
3535
export * from "./SingleLineCommentBlock.jsx";
3636
export * from "./SourceFile.js";
3737
export * from "./SwitchStatement.jsx";
38+
export * from "./TryStatement.jsx";
3839
export * from "./TsConfigJson.js";
3940
export * from "./TypeDeclaration.js";
4041
export * from "./TypeRefContext.jsx";

0 commit comments

Comments
 (0)