CursorPool
← 返回首页
Shopify App Development logo

Shopify App Development

11

使用 TypeScript 按最佳实践开发 Shopify App Extensions。

2 条规则

# Converting Shopify Extensions from JavaScript to TypeScript

## Overview

Shopify Checkout UI Extensions can be written in TypeScript (`.tsx`) instead of JavaScript (`.jsx`). This provides better type safety and IDE support.

## Conversion Steps

### 1. Rename the file

- Change `src/Checkout.jsx` → `src/Checkout.tsx`

### 2. Update `shopify.extension.toml`

```toml
[[extensions.targeting]]
module = "./src/Checkout.tsx"  # Changed from .jsx
target = "purchase.checkout.block.render"
```

### 3. Update `shopify.d.ts`

```typescript
import "@shopify/ui-extensions";

declare module "./src/Checkout.tsx" {
  // Changed from .jsx
  const shopify: import("@shopify/ui-extensions/purchase.checkout.block.render").Api;
  const globalThis: { shopify: typeof shopify };
}
```

### 4. Update `tsconfig.json`

Remove `checkJs` and `allowJs`, add `strict`:

```json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "preact",
    "target": "ES2020",
    "strict": true, // Add this
    "moduleResolution": "bundler",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "baseUrl": "../..",
    "paths": {
      "@lib/utils": ["./lib/utils.ts"],
      "@lib/*": ["./lib/*"]
    }
  },
  "include": ["./src", "./shopify.d.ts", "../../lib/**/*"]
}
```

### 5. Add TypeScript Types

Add type definitions for your settings and component props:

```typescript
// Common types
type PaddingKeyword = "none" | "small" | "small-100" | "base" | "large" | "large-100";
type JustifyContent = "start" | "center" | "end";
type Tone = "auto" | "neutral" | "info" | "success" | "warning" | "critical" | "custom";

// Example usage
const paddingMap: Record<string, PaddingKeyword> = {
  none: "none",
  extraTight: "small"
  // ...
};
const safePadding: PaddingKeyword = paddingMap[paddingValue] || "none";
```

### 6. Type Your Variables

```typescript
// Before (JSX)
const text = String(settings?.text || "");
const padding = String(settings?.padding || "none");

// After (TSX)
const text: string = String(settings?.text || "");
const padding: string = String(settings?.padding || "none");
const safePadding: PaddingKeyword = paddingMap[padding] || "none";
```

### 7. Type JSX Elements

```typescript
// Before
let content;
if (condition) {
  content = <s-text>Hello</s-text>;
}

// After
let content: JSX.Element | null;
if (condition) {
  content = <s-text>Hello</s-text>;
} else {
  content = null;
}
```

## Benefits

1. **Type Safety**: Catch errors at compile time
2. **Better IDE Support**: Autocomplete, refactoring, navigation
3. **Self-Documenting**: Types serve as documentation
4. **Easier Refactoring**: TypeScript helps ensure changes are consistent

## Common Type Patterns

### Settings Values

```typescript
// Settings from shopify.settings.value can be: string | number | boolean | null | undefined
const text = String(settings?.text || "");
const number = typeof settings?.number === "number" ? settings.number : 0;
const enabled = Boolean(settings?.enabled);
```

### Padding/Spacing Values

```typescript
type PaddingKeyword = "none" | "small" | "small-100" | "base" | "large" | "large-100";
const paddingMap: Record<string, PaddingKeyword> = {
  none: "none",
  extraTight: "small",
  tight: "small-100",
  base: "base",
  loose: "large",
  extraLoose: "large-100"
};
```

### Tone/Appearance Values

```typescript
type Tone = "auto" | "neutral" | "info" | "success" | "warning" | "critical" | "custom";
const toneMap: Record<string, Tone> = {
  normal: "auto",
  accent: "neutral",
  subdued: "neutral",
  info: "info",
  success: "success",
  warning: "warning",
  critical: "critical",
  decorative: "custom"
};
```

## Example: Complete Conversion

**Before (Checkout.jsx):**

```jsx
import "@shopify/ui-extensions/preact";
import { render } from "preact";

export default async () => {
  render(<Extension />, document.body);
};

function Extension() {
  const settings = shopify.settings.value;
  const text = String(settings?.text || "");
  return <s-text>{text}</s-text>;
}
```

**After (Checkout.tsx):**

```tsx
import "@shopify/ui-extensions/preact";
import { render } from "preact";

export default async () => {
  render(<Extension />, document.body);
};

function Extension() {
  const settings = shopify.settings.value;
  const text: string = String(settings?.text || "");
  return <s-text>{text}</s-text>;
}
```

## Notes

- Shopify's extension bundler supports TypeScript and will compile `.tsx` files
- The `shopify` global object is typed via `shopify.d.ts`
- Path aliases (`@lib/*`) work the same in TypeScript
- All Polaris web components are typed via `@shopify/ui-extensions/preact`