Skip to main content

TypeScript migration guide

This guide covers the gradual migration of the SuperDoc monorepo from JavaScript to TypeScript.

Quick start

Infrastructure is ready. You can now:
  • Create new files as .ts or .vue with <script lang="ts">
  • Migrate existing .js files when making significant changes
  • Mix .js and .ts files freely - they work together
Type checking: npm run types:check --workspace=packages/super-editor

When to migrate a file

Convert to TypeScript when:
  1. Making significant changes - Refactoring or adding major features
  2. Creating new files - All new files should be .ts (or .vue with TypeScript)
  3. Working on core logic - Prioritize type safety in critical paths
  4. Fixing type-related bugs - Convert to prevent similar issues
Don’t migrate: Files you’re not actively working on. Let migration happen naturally.

How to migrate

1. Basic function

Before (helper.js):
export function formatDate(date) {
  return date.toISOString();
}
After (helper.ts):
export function formatDate(date: Date): string {
  return date.toISOString();
}

2. Vue components

Before:
<script>
export default {
  props: {
    title: String,
  },
};
</script>
After with <script setup> (recommended):
<script setup lang="ts">
defineProps<{
  title: string;
}>();
</script>
After with Options API:
<script lang="ts">
export default {
  props: {
    title: {
      type: String,
      required: true,
    },
  },
};
</script>

3. Prefer unknown over any

// Instead of this
function process(data: any) {
  return data.value;
}

// Do this
function process(data: unknown) {
  if (typeof data === 'object' && data !== null && 'value' in data) {
    return (data as { value: any }).value;
  }
}

Type checking

npm run types:check --workspace=packages/super-editor

Root level

npm run type-check
Note: Root-level checking may show path alias errors during migration. Use package-specific checking for accurate results.

Common issues

Type errors in JavaScript files

Ensure checkJs: false in your tsconfig. Only .ts files are type-checked during migration.

Build fails after migrating a file

Run a clean build:
npm run clean:packages && npm run build

Migration priority

Suggested order:
  1. Utilities and helpers - Pure functions, easy to type
  2. Core types - Type definitions and interfaces
  3. Extensions - Self-contained modules
  4. Commands - Clear input/output
  5. Components - Vue components (can stay JS longer)

Testing

  • Tests can stay as .js files initially
  • They’ll import TypeScript modules normally
  • Run tests as usual: npm test

Advanced patterns

ProseMirror types

import { Node as PMNode, Mark } from 'prosemirror-model';
import { EditorState, Transaction } from 'prosemirror-state';

export function updateNodeAttrs(node: PMNode, attrs: Record<string, any>): PMNode {
  return node.type.create({ ...node.attrs, ...attrs }, node.content);
}

Extension pattern

import { Extension } from 'superdoc/super-editor';

export interface MyExtensionOptions {
  enabled?: boolean;
  customValue?: string;
}

export class MyExtension extends Extension<MyExtensionOptions> {
  name = 'myExtension';

  defaultOptions: MyExtensionOptions = {
    enabled: true,
    customValue: 'default',
  };

  // Implementation
}

Discriminated unions

type CommandResult = { success: true; data: any } | { success: false; error: string };

function handleResult(result: CommandResult) {
  if (result.success) {
    console.log(result.data); // TypeScript knows this exists
  } else {
    console.error(result.error); // TypeScript knows this exists
  }
}

Resources


Questions?

  1. Check this guide first
  2. Look at packages/ai for a fully migrated example
  3. Ask in team chat
  4. Update this guide with new learnings

Last Updated: Phase 0 Complete (Infrastructure Setup)