Repository: alloy-framework/alloy
Pull request #125 (2025-04-18 22:37:37)

Type context
Follow up to #120 and #124 which automatically define the current context (value vs type)

Automatically enter this context when in
- interface
- interface expression
- function type

github-actions[bot]: <!--chronus-github-change-commenter-->
:x: There is undocummented changes. Run `chronus add` to add a changeset or [click here](https://github.com/timotheeguerin/alloy/new/type-context?filename=.chronus/changes/type-context-2025-3-18-22-37-56.md&value=---%0A%23%20Change%20versionKind%20to%20one%20of%3A%20breaking%2C%20feature%2C%20fix%2C%20internal%0AchangeKind%3A%20fix%0Apackages%3A%0A%20%20-%20%22%40alloy-js%2Ftypescript%22%0A---%0A%0AType%20context).

**The following packages have changes but are not documented.**
 - :x:`@alloy-js/typescript`

<details>
<summary>Show changes</summary>


</details>

pkg-pr-new[bot]: 
[Open in StackBlitz](https://pkg.pr.new/template/6c5f3f0b-a643-4473-9126-fb234c5b5491)


<details><summary><b>@alloy-js/babel-plugin</b></summary><p>

```
npm i https://pkg.pr.new/alloy-framework/alloy/@alloy-js/babel-plugin@125
```

</p></details>
      
    

<details><summary><b>@alloy-js/babel-plugin-jsx-dom-expressions</b></summary><p>

```
npm i https://pkg.pr.new/alloy-framework/alloy/@alloy-js/babel-plugin-jsx-dom-expressions@125
```

</p></details>
      
    

<details><summary><b>@alloy-js/cli</b></summary><p>

```
npm i https://pkg.pr.new/alloy-framework/alloy/@alloy-js/cli@125
```

</p></details>
      
    

<details><summary><b>@alloy-js/babel-preset</b></summary><p>

```
npm i https://pkg.pr.new/alloy-framework/alloy/@alloy-js/babel-preset@125
```

</p></details>
      
    

<details><summary><b>@alloy-js/core</b></summary><p>

```
npm i https://pkg.pr.new/alloy-framework/alloy/@alloy-js/core@125
```

</p></details>
      
    

<details><summary><b>@alloy-js/create</b></summary><p>

```
npm i https://pkg.pr.new/alloy-framework/alloy/@alloy-js/create@125
```

</p></details>
      
    

<details><summary><b>@alloy-js/csharp</b></summary><p>

```
npm i https://pkg.pr.new/alloy-framework/alloy/@alloy-js/csharp@125
```

</p></details>
      
    

<details><summary><b>@alloy-js/java</b></summary><p>

```
npm i https://pkg.pr.new/alloy-framework/alloy/@alloy-js/java@125
```

</p></details>
      
    

<details><summary><b>@alloy-js/json</b></summary><p>

```
npm i https://pkg.pr.new/alloy-framework/alloy/@alloy-js/json@125
```

</p></details>
      
    

<details><summary><b>@alloy-js/prettier-plugin-alloy</b></summary><p>

```
npm i https://pkg.pr.new/alloy-framework/alloy/@alloy-js/prettier-plugin-alloy@125
```

</p></details>
      
    

<details><summary><b>@alloy-js/rollup-plugin</b></summary><p>

```
npm i https://pkg.pr.new/alloy-framework/alloy/@alloy-js/rollup-plugin@125
```

</p></details>
      
    

<details><summary><b>@alloy-js/typescript</b></summary><p>

```
npm i https://pkg.pr.new/alloy-framework/alloy/@alloy-js/typescript@125
```

</p></details>
      
    

_commit: <a href="https://github.com/alloy-framework/alloy/runs/40803803300"><code>1ca3e66</code></a>_


Patch:
diff --git a/.chronus/changes/type-context-2025-3-18-22-37-56.md b/.chronus/changes/type-context-2025-3-18-22-37-56.md
new file mode 100644
index 0000000..45633a0
--- /dev/null
+++ b/.chronus/changes/type-context-2025-3-18-22-37-56.md
@@ -0,0 +1,8 @@
+---
+# Change versionKind to one of: breaking, feature, fix, internal
+changeKind: feature
+packages:
+  - "@alloy-js/typescript"
+---
+
+Automatically enter a type context when inside of interface, function type, type declaration, etc. This allow referenced used in there to automatically add the `type` keyword in the import
diff --git a/packages/docs/package.json b/packages/docs/package.json
index 1778e8e..184fd37 100644
--- a/packages/docs/package.json
+++ b/packages/docs/package.json
@@ -7,8 +7,8 @@
     "dev": "astro dev",
     "start": "astro dev",
     "build-production": "pnpm generate-docs && astro check && astro build",
-    "generate-docs": "pnpm run build-scripts && node dist-build/scripts/build-json.js",
-    "build-scripts": "tsc -p scripts.tsconfig.json",
+    "generate-docs": "pnpm check-scripts && tsx scripts/build-json.ts",
+    "check-scripts": "tsc -p scripts.tsconfig.json",
     "preview": "astro preview",
     "astro": "astro"
   },
@@ -18,7 +18,8 @@
     "astro": "^5.6.1",
     "astro-expressive-code": "^0.40.2",
     "sharp": "^0.34.1",
-    "typescript": "catalog:"
+    "typescript": "catalog:",
+    "tsx": "catalog:"
   },
   "devDependencies": {
     "@alloy-js/core": "workspace:~",
diff --git a/packages/docs/scripts.tsconfig.json b/packages/docs/scripts.tsconfig.json
index ff1d876..9909d45 100644
--- a/packages/docs/scripts.tsconfig.json
+++ b/packages/docs/scripts.tsconfig.json
@@ -1,9 +1,8 @@
 {
   "extends": "../../tsconfig.base.json",
   "compilerOptions": {
-    "outDir": "./dist-build"
+    "noEmit": true
   },
-
   "include": ["scripts/**/*.ts"],
   "exclude": ["node_modules", "dist"]
 }
diff --git a/packages/docs/scripts/build-json.ts b/packages/docs/scripts/build-json.ts
index 82ae44c..2a332c1 100644
--- a/packages/docs/scripts/build-json.ts
+++ b/packages/docs/scripts/build-json.ts
@@ -24,11 +24,11 @@ import {
 import { ApiModelContext } from "./contexts/api-model.js";
 import { ContentRootDir } from "./contexts/content-root-dir.js";
 
-const rootDir = resolve(import.meta.dirname, "../../src/content/docs");
+const rootDir = resolve(import.meta.dirname, "../src/content/docs");
 
 const docPath = resolve(rootDir, "reference");
 
-const packagesPath = resolve(import.meta.dirname, "../../../");
+const packagesPath = resolve(import.meta.dirname, "../../");
 
 const apiModel: ApiModel = new ApiModel();
 const apiPackages = {
@@ -238,6 +238,13 @@ function queryApis(apiModel: ApiModel): DocumentationStructure {
             if ((member as ApiFunction).parameters.length > 0) {
               const propsTypeRef = (member as ApiFunction).parameters[0]
                 .parameterTypeExcerpt.spannedTokens[0].canonicalReference;
+              if (propsTypeRef === undefined) {
+                // https://github.com/alloy-framework/alloy/issues/128
+                console.log(
+                  `warn: Cannot find props type reference for ${member.displayName}`,
+                );
+                continue;
+              }
               const model = apiModel.resolveDeclarationReference(
                 propsTypeRef!,
                 undefined,
diff --git a/packages/typescript/src/components/FunctionBase.tsx b/packages/typescript/src/components/FunctionBase.tsx
index 672c078..8beceb1 100644
--- a/packages/typescript/src/components/FunctionBase.tsx
+++ b/packages/typescript/src/components/FunctionBase.tsx
@@ -19,6 +19,7 @@ import {
   TSOutputSymbol,
   TSSymbolFlags,
 } from "../symbols/index.js";
+import { TypeRefContext } from "./TypeRefContext.jsx";
 
 const functionParametersTag = Symbol();
 const typeParametersTag = Symbol();
@@ -124,7 +125,9 @@ function parameter(param: DeclaredParameterDescriptor) {
       {param.symbol.name}
       <Show when={!!param.optional}>?</Show>
       <Show when={!!param.type}>
-        <indent>: {param.type}</indent>
+        <indent>
+          : <TypeRefContext>{param.type}</TypeRefContext>
+        </indent>
       </Show>
     </group>
   );
@@ -255,7 +258,10 @@ function typeParameter(param: DeclaredTypeParameterDescriptor) {
       <Show when={!!param.extends}>
         {" "}
         extends
-        <indent> {param.extends}</indent>
+        <indent>
+          {" "}
+          <TypeRefContext>{param.extends}</TypeRefContext>
+        </indent>
       </Show>
     </group>
   );
diff --git a/packages/typescript/src/components/FunctionType.tsx b/packages/typescript/src/components/FunctionType.tsx
index f4e3231..deaf09a 100644
--- a/packages/typescript/src/components/FunctionType.tsx
+++ b/packages/typescript/src/components/FunctionType.tsx
@@ -16,9 +16,10 @@ import {
 } from "./FunctionBase.jsx";
 import { JSDoc } from "./JSDoc.jsx";
 import { JSDocParams } from "./JSDocParam.jsx";
+import { ensureTypeRefContext } from "./TypeRefContext.jsx";
 
 /**
- * Options for {@link (FunctionType:namespace)} component.
+ * Options for {@link FunctionType} component.
  */
 export interface FunctionTypeProps extends CallSignatureProps {
   /** If the method is async */
@@ -29,6 +30,43 @@ export interface FunctionTypeProps extends CallSignatureProps {
   readonly children?: Children;
 }
 
+const FunctionTypeComponent = ensureTypeRefContext(
+  (props: FunctionTypeProps) => {
+    const children = childrenArray(() => props.children);
+    const typeParametersChildren =
+      findKeyedChild(children, TypeParameters.tag) ?? undefined;
+    const parametersChildren =
+      findKeyedChild(children, FunctionParameters.tag) ?? undefined;
+    const returnType = getReturnType(props.returnType ?? "void", {
+      async: props.async,
+    });
+
+    const callSignatureProps = getCallSignatureProps(props, {
+      parametersChildren,
+      typeParametersChildren,
+    });
+
+    return (
+      <>
+        <Show when={Boolean(props.doc)}>
+          <JSDoc>
+            {props.doc && <Prose children={props.doc} />}
+            {Array.isArray(props.parameters) && (
+              <JSDocParams parameters={props.parameters} />
+            )}
+          </JSDoc>
+          <hbr />
+        </Show>
+        <Scope kind="function">
+          <CallSignature {...callSignatureProps} returnType={null} />
+          {" => "}
+          {returnType}
+        </Scope>
+      </>
+    );
+  },
+);
+
 /**
  * A TypeScript function declaration.
  *
@@ -46,43 +84,10 @@ export interface FunctionTypeProps extends CallSignatureProps {
  * 2. As raw content via the `parametersChildren` or `typeParametersChildren`
  *    props.
  * 3. As a child of this component via the
- *    {@link (FunctionType:namespace).Parameters} or
- *    {@link (FunctionType:namespace).TypeParameters} components.
+ *    {@link FunctionType.Parameters} or
+ *    {@link FunctionType.TypeParameters} components.
  */
-export function FunctionType(props: FunctionTypeProps) {
-  const children = childrenArray(() => props.children);
-  const typeParametersChildren =
-    findKeyedChild(children, TypeParameters.tag) ?? undefined;
-  const parametersChildren =
-    findKeyedChild(children, FunctionParameters.tag) ?? undefined;
-  const returnType = getReturnType(props.returnType ?? "void", {
-    async: props.async,
-  });
-
-  const callSignatureProps = getCallSignatureProps(props, {
-    parametersChildren,
-    typeParametersChildren,
-  });
-
-  return (
-    <>
-      <Show when={Boolean(props.doc)}>
-        <JSDoc>
-          {props.doc && <Prose children={props.doc} />}
-          {Array.isArray(props.parameters) && (
-            <JSDocParams parameters={props.parameters} />
-          )}
-        </JSDoc>
-        <hbr />
-      </Show>
-      <Scope kind="function">
-        <CallSignature {...callSignatureProps} returnType={null} />
-        {" => "}
-        {returnType}
-      </Scope>
-    </>
-  );
-}
-
-FunctionType.TypeParameters = TypeParameters;
-FunctionType.Parameters = FunctionTypeParameters;
+export const FunctionType = Object.assign(FunctionTypeComponent, {
+  TypeParameters,
+  Parameters: FunctionTypeParameters,
+});
diff --git a/packages/typescript/src/components/ImportStatement.tsx b/packages/typescript/src/components/ImportStatement.tsx
index 508ca01..472bbac 100644
--- a/packages/typescript/src/components/ImportStatement.tsx
+++ b/packages/typescript/src/components/ImportStatement.tsx
@@ -92,7 +92,7 @@ export function ImportStatement(props: ImportStatementProps) {
 
     if (namedImportSymbols.length > 0) {
       const allNamedImportsAreTypes = namedImportSymbols.every(
-        (nis) => nis.target.tsFlags & TSSymbolFlags.TypeSymbol,
+        (nis) => nis.local.tsFlags & TSSymbolFlags.TypeSymbol,
       );
 
       if (allNamedImportsAreTypes) {
diff --git a/packages/typescript/src/components/Interface.tsx b/packages/typescript/src/components/Interface.tsx
index 89aa6a1..842049c 100644
--- a/packages/typescript/src/components/Interface.tsx
+++ b/packages/typescript/src/components/Interface.tsx
@@ -16,6 +16,7 @@ import { JSDoc } from "./JSDoc.jsx";
 
 import { MemberDeclaration } from "./MemberDeclaration.jsx";
 import { PropertyName } from "./PropertyName.jsx";
+import { ensureTypeRefContext } from "./TypeRefContext.jsx";
 
 export interface InterfaceDeclarationProps extends BaseDeclarationProps {
   extends?: Children;
@@ -30,47 +31,51 @@ export interface InterfaceDeclarationProps extends BaseDeclarationProps {
  * `default` boolean props determine whether and how this symbol is exported
  * from the package.
  */
-export function InterfaceDeclaration(props: InterfaceDeclarationProps) {
-  const extendsPart = props.extends ? <> extends {props.extends}</> : "";
-  const flags = OutputSymbolFlags.StaticMemberContainer;
+export const InterfaceDeclaration = ensureTypeRefContext(
+  (props: InterfaceDeclarationProps) => {
+    const extendsPart = props.extends ? <> extends {props.extends}</> : "";
+    const flags = OutputSymbolFlags.StaticMemberContainer;
 
-  return (
-    <>
-      <Show when={Boolean(props.doc)}>
-        <JSDoc children={props.doc} />
-        <hbr />
-      </Show>
-      <Declaration {...props} nameKind="interface" flags={flags} kind="type">
-        interface <Name />
-        {extendsPart}{" "}
-        <InterfaceExpression>{props.children}</InterfaceExpression>
-      </Declaration>
-    </>
-  );
-}
+    return (
+      <>
+        <Show when={Boolean(props.doc)}>
+          <JSDoc children={props.doc} />
+          <hbr />
+        </Show>
+        <Declaration {...props} nameKind="interface" flags={flags} kind="type">
+          interface <Name />
+          {extendsPart}{" "}
+          <InterfaceExpression>{props.children}</InterfaceExpression>
+        </Declaration>
+      </>
+    );
+  },
+);
 
 export interface InterfaceExpressionProps {
   children?: Children;
 }
 
-export function InterfaceExpression(props: InterfaceExpressionProps) {
-  const parentMemberSym = useMemberDeclaration();
+export const InterfaceExpression = ensureTypeRefContext(
+  (props: InterfaceExpressionProps) => {
+    const parentMemberSym = useMemberDeclaration();
 
-  if (parentMemberSym) {
-    parentMemberSym.binder.addStaticMembersToSymbol(parentMemberSym);
-  }
-  return (
-    <group>
-      <Wrap
-        when={!!parentMemberSym}
-        with={MemberScope}
-        props={{ owner: parentMemberSym! }}
-      >
-        <Block>{props.children}</Block>
-      </Wrap>
-    </group>
-  );
-}
+    if (parentMemberSym) {
+      parentMemberSym.binder.addStaticMembersToSymbol(parentMemberSym);
+    }
+    return (
+      <group>
+        <Wrap
+          when={!!parentMemberSym}
+          with={MemberScope}
+          props={{ owner: parentMemberSym! }}
+        >
+          <Block>{props.children}</Block>
+        </Wrap>
+      </group>
+    );
+  },
+);
 
 export interface InterfaceMemberProps {
   name?: string;
diff --git a/packages/typescript/src/components/Reference.tsx b/packages/typescript/src/components/Reference.tsx
index 6d278c7..e610cb3 100644
--- a/packages/typescript/src/components/Reference.tsx
+++ b/packages/typescript/src/components/Reference.tsx
@@ -1,4 +1,5 @@
 import { Refkey } from "@alloy-js/core";
+import { isTypeRefContext } from "../context/type-ref-context.js";
 import { ref } from "../symbols/index.js";
 
 export interface ReferenceProps {
@@ -14,7 +15,8 @@ export interface ReferenceProps {
 }
 
 export function Reference({ refkey, type }: ReferenceProps) {
-  const reference = ref(refkey, { type });
+  const inTypeRef = isTypeRefContext();
+  const reference = ref(refkey, { type: type ?? inTypeRef });
 
   return <>{reference}</>;
 }
diff --git a/packages/typescript/src/components/TypeDeclaration.tsx b/packages/typescript/src/components/TypeDeclaration.tsx
index 0dd8eef..818b78e 100644
--- a/packages/typescript/src/components/TypeDeclaration.tsx
+++ b/packages/typescript/src/components/TypeDeclaration.tsx
@@ -1,19 +1,22 @@
 import { Name, Show } from "@alloy-js/core";
 import { BaseDeclarationProps, Declaration } from "./Declaration.js";
 import { JSDoc } from "./JSDoc.jsx";
+import { ensureTypeRefContext } from "./TypeRefContext.jsx";
 
 export interface TypeDeclarationProps extends BaseDeclarationProps {}
 
-export function TypeDeclaration(props: TypeDeclarationProps) {
-  return (
-    <>
-      <Show when={Boolean(props.doc)}>
-        <JSDoc children={props.doc} />
-        <hbr />
-      </Show>
-      <Declaration {...props} nameKind="type">
-        type <Name /> = {props.children};
-      </Declaration>
-    </>
-  );
-}
+export const TypeDeclaration = ensureTypeRefContext(
+  (props: TypeDeclarationProps) => {
+    return (
+      <>
+        <Show when={Boolean(props.doc)}>
+          <JSDoc children={props.doc} />
+          <hbr />
+        </Show>
+        <Declaration {...props} nameKind="type">
+          type <Name /> = {props.children};
+        </Declaration>
+      </>
+    );
+  },
+);
diff --git a/packages/typescript/src/components/TypeRefContext.tsx b/packages/typescript/src/components/TypeRefContext.tsx
new file mode 100644
index 0000000..c32c9c2
--- /dev/null
+++ b/packages/typescript/src/components/TypeRefContext.tsx
@@ -0,0 +1,49 @@
+import type { Children } from "@alloy-js/core/jsx-runtime";
+import {
+  isTypeRefContext,
+  TypeRefContext as TypeRefContextDef,
+} from "../context/type-ref-context.jsx";
+
+export interface TypeRefContextProps {
+  /**
+   * Children
+   */
+  children: Children;
+}
+
+/**
+ * Set the current context of reference to be type reference.
+ *
+ * @remarks
+ * References used inside the children of this component will be treated as type reference {@link Reference}.
+ *
+ * Prefer using {@link ensureTypeRefContext} when wrapping the whole component to reduce tree nodes.
+ */
+export const TypeRefContext = ({ children }: TypeRefContextProps) => {
+  return (
+    <TypeRefContextDef.Provider value={{ type: true }}>
+      {children}
+    </TypeRefContextDef.Provider>
+  );
+};
+
+/** Ensure the current component is inside a type ref context.
+ * If not it will wrap in a {@link TypeRefContext} component.
+ * If yes it will not add an extra node and return the original component.
+ */
+export function ensureTypeRefContext<A extends unknown[]>(
+  fn: (...args: A) => Children,
+): (...args: A) => Children {
+  return (...args: A) => {
+    const ref = isTypeRefContext();
+    if (!ref) {
+      return (
+        <TypeRefContextDef.Provider value={{ type: true }}>
+          {fn(...args)}
+        </TypeRefContextDef.Provider>
+      );
+    }
+
+    return fn(...args);
+  };
+}
diff --git a/packages/typescript/src/components/VarDeclaration.tsx b/packages/typescript/src/components/VarDeclaration.tsx
index fdcb4ce..acd5eb5 100644
--- a/packages/typescript/src/components/VarDeclaration.tsx
+++ b/packages/typescript/src/components/VarDeclaration.tsx
@@ -10,6 +10,7 @@ import { useTSNamePolicy } from "../name-policy.js";
 import { createTSSymbol } from "../symbols/index.js";
 import { BaseDeclarationProps } from "./Declaration.js";
 import { JSDoc } from "./JSDoc.jsx";
+import { TypeRefContext } from "./TypeRefContext.jsx";
 
 export interface VarDeclarationProps extends BaseDeclarationProps {
   const?: boolean;
@@ -24,7 +25,8 @@ export function VarDeclaration(props: VarDeclarationProps) {
     props.var ? "var"
     : props.let ? "let"
     : "const";
-  const type = props.type ? <>: {props.type}</> : undefined;
+  const type =
+    props.type ? <TypeRefContext>: {props.type}</TypeRefContext> : undefined;
   const name = useTSNamePolicy().getName(props.name, "variable");
   const sym = createTSSymbol({
     name: name,
diff --git a/packages/typescript/src/components/index.ts b/packages/typescript/src/components/index.ts
index 0566c21..f6e9194 100644
--- a/packages/typescript/src/components/index.ts
+++ b/packages/typescript/src/components/index.ts
@@ -36,5 +36,6 @@ export * from "./SourceFile.js";
 export * from "./SwitchStatement.jsx";
 export * from "./TsConfigJson.js";
 export * from "./TypeDeclaration.js";
+export * from "./TypeRefContext.jsx";
 export * from "./ValueExpression.js";
 export * from "./VarDeclaration.js";
diff --git a/packages/typescript/src/components/stc/index.ts b/packages/typescript/src/components/stc/index.ts
index 1db3dab..c9c91cf 100644
--- a/packages/typescript/src/components/stc/index.ts
+++ b/packages/typescript/src/components/stc/index.ts
@@ -42,5 +42,6 @@ export const CaseClause = stc(base.CaseClause);
 export const TSConfigJson = stc(base.TSConfigJson);
 export const FunctionType = stc(base.FunctionType);
 export const TypeDeclaration = stc(base.TypeDeclaration);
+export const TypeRefContext = stc(base.TypeRefContext);
 export const ValueExpression = stc(base.ValueExpression);
 export const VarDeclaration = stc(base.VarDeclaration);
diff --git a/packages/typescript/src/context/type-ref-context.tsx b/packages/typescript/src/context/type-ref-context.tsx
new file mode 100644
index 0000000..c6974c4
--- /dev/null
+++ b/packages/typescript/src/context/type-ref-context.tsx
@@ -0,0 +1,19 @@
+import { ComponentContext, createContext, useContext } from "@alloy-js/core";
+
+interface TypeRefContext {
+  /** If in a type context */
+  type?: boolean;
+}
+
+/**
+ * Provides scopes for instance and static private members.
+ */
+export const TypeRefContext: ComponentContext<TypeRefContext> =
+  createContext<TypeRefContext>();
+
+/**
+ * @returns 'true' if in a type context, 'false' if in a value context.
+ */
+export function isTypeRefContext(): boolean {
+  return useContext(TypeRefContext)?.type ?? false;
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index cd07cc3..e3d9606 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -96,6 +96,9 @@ catalogs:
     rollup:
       specifier: ^4.39.0
       version: 4.39.0
+    tsx:
+      specifier: ^4.19.3
+      version: 4.19.3
     typescript:
       specifier: ^5.8.3
       version: 5.8.3
@@ -415,6 +418,9 @@ importers:
       sharp:
         specifier: ^0.34.1
         version: 0.34.1
+      tsx:
+        specifier: 'catalog:'
+        version: 4.19.3
       typescript:
         specifier: 'catalog:'
         version: 5.8.3
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
index add9933..37a29e4 100644
--- a/pnpm-workspace.yaml
+++ b/pnpm-workspace.yaml
@@ -33,6 +33,7 @@ catalog:
   prettier: ^3.5.3
   rimraf: ^6.0.1
   rollup: ^4.39.0
+  tsx: ^4.19.3
   typescript-eslint: "^8.29.1"
   typescript: ^5.8.3
   validate-html-nesting: ^1.2.1


Test patch:
diff --git a/packages/typescript/test/imports.test.tsx b/packages/typescript/test/imports.test.tsx
index 44f27df..804d96f 100644
--- a/packages/typescript/test/imports.test.tsx
+++ b/packages/typescript/test/imports.test.tsx
@@ -10,6 +10,7 @@ import "@alloy-js/core/testing";
 import { describe, it } from "vitest";
 import * as ts from "../src/components/index.js";
 import { Reference } from "../src/components/Reference.js";
+import { TypeRefContext } from "../src/context/type-ref-context.js";
 import { tsNameConflictResolver } from "../src/name-conflict-resolver.js";
 import { assertFileContents } from "./utils.js";
 
@@ -440,4 +441,34 @@ describe("type imports", () => {
     `,
     });
   });
+
+  it("infer if a type reference from the typescript context", () => {
+    const { component, TypeA } = mkTestFile("test1.ts");
+    const res = render(
+      <Output>
+        {component}
+        <ts.SourceFile path="test2.ts">
+          <TypeRefContext.Provider value={{ type: true }}>
+            <List>
+              <>
+                type A = <Reference refkey={TypeA} />
+              </>
+              <>
+                type B = <Reference refkey={TypeA} />
+              </>
+            </List>
+          </TypeRefContext.Provider>
+        </ts.SourceFile>
+      </Output>,
+    );
+
+    assertFileContents(res, {
+      "test2.ts": `
+      import type { TypeA } from "./test1.js";
+
+      type A = TypeA
+      type B = TypeA
+    `,
+    });
+  });
 });
diff --git a/packages/typescript/test/var-declaration.test.tsx b/packages/typescript/test/var-declaration.test.tsx
index ed01c45..9f0524e 100644
--- a/packages/typescript/test/var-declaration.test.tsx
+++ b/packages/typescript/test/var-declaration.test.tsx
@@ -43,7 +43,7 @@ it("works end-to-end", () => {
       type TestType = "hello" | "goodbye";
     `,
     "test.ts": `
-      import { TestType } from "./types.js";
+      import type { TestType } from "./types.js";
 
       export let hi: TestType = "hello";
     `,