Typescript
-
Ditch Jest for Vitest: A Ready-Made Migration Prompt
If you’ve ever sat there watching Jest crawl through your TypeScript test suite, you know pain. I mean, I know your pain.
When Switching to Vitest, and the speed difference is genuinely dramatic. The answers to why it’s slow are easy to figure out. There’s plenty of explanations on that, so I’ll leave that to you to go look up why.
I put together a prompt you can hand to Claude (or any AI assistant) that will handle the migration for you. Let me know how it goes!
The Migration Prompt
Convert all Jest tests in this project to Vitest. Here's what to do: ## Setup 1. Remove Jest dependencies (`jest`, `ts-jest`, `@types/jest`, `babel-jest`, any jest presets) 2. Install Vitest: `pnpm add -D vitest` 3. Remove `jest.config.*` files 4. Add a `test` section to `vite.config.ts` (or create `vitest.config.ts` if no Vite config exists): import { defineConfig } from 'vitest/config' export default defineConfig({ test: { globals: true, }, }) 5. Update the `test` script in `package.json` to `vitest` ## Test File Migration For every test file: 1. Replace imports — Remove any `import ... from '@jest/globals'`. If `globals: true` is set, no imports needed. Otherwise add: import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest' 2. Replace `jest` with `vi` everywhere: - jest.fn() → vi.fn() - jest.mock() → vi.mock() - jest.spyOn() → vi.spyOn() - jest.useFakeTimers() → vi.useFakeTimers() - jest.useRealTimers() → vi.useRealTimers() - jest.advanceTimersByTime() → vi.advanceTimersByTime() - jest.clearAllMocks() → vi.clearAllMocks() - jest.resetAllMocks() → vi.resetAllMocks() - jest.restoreAllMocks() → vi.restoreAllMocks() - jest.requireActual() → vi.importActual() (note: async in Vitest) 3. Fix mock hoisting — vi.mock() is hoisted automatically, but variables used in the mock factory must be prefixed with `vi` or declared inside the factory. 4. Fix jest.requireActual — This becomes vi.importActual() and returns a Promise: // Jest jest.mock('./utils', () => ({ ...jest.requireActual('./utils'), fetchData: jest.fn(), })) // Vitest vi.mock('./utils', async () => ({ ...(await vi.importActual('./utils')), fetchData: vi.fn(), })) 5. Snapshot tests work the same way. No changes needed. 6. Timer mocks — same API after the jest → vi rename. 7. Module mocks with __mocks__ directories work identically. ## TypeScript Config Add Vitest types to tsconfig.json: { "compilerOptions": { "types": ["vitest/globals"] } } ## After Migration 1. Delete leftover Jest config files 2. Update CI config to use `vitest run` 3. Run tests and fix any remaining failures
If you’re still on Jest and your TypeScript test suite is dragging, give Vitest a shot. The migration is low-risk, the speed improvements are real, and with the prompt above, the hardest parts are handled. ☕
/ Development / Testing / Typescript / Vitest / Jest
-
Why Svelte 5 Wants `let` Instead of `const` (And Why Your Linter Is Confused)
If you’ve been working with Svelte 5 and a linter like Biome, you might have run into this sitauation:
use
constinstead ofletHowever, Svelte actually needs that
let.Have you ever wondered why this is?
Here is an attempt to explain it to you.
Svelte 5’s reactivity model is different from the rest of the TypeScript world, and some of our tooling hasn’t quite caught up yet.
The
constRule Everyone KnowsIn standard TypeScript and React, the
prefer-construle is a solid best practice.If you declare a variable and never reassign it, use
const. It communicates intent clearly: this binding won’t change. You would thinkBut there is confusion when it comes to objects that are defined as
const.Let’s take a look at a React example:
// React — const makes perfect sense here const [count, setCount] = useState(0); const handleClick = () => setCount(count + 1);count is a number (primitive). setCount is a function.
Neither can modify itself so it makes sense to use
const.Svelte 5 Plays by Different Rules
Svelte 5 introduced runes — reactive primitives like
$state(),$derived(), and$props()that bring fine-grained reactivity directly into JavaScript.Svelte compiler transforms these declarations into getters and setters behind the scenes.
The value does get reassigned, even if your code looks like a simple variable.
<script lang="ts"> let count = $state(0); </script> <button onclick={() => count++}> Clicked {count} times </button>Biome Gets This Wrong
But they are trying to make it right. Biome added experimental Svelte support in v2.3.0, but it has a significant limitation: it only analyzes the
<script>block in isolation.It doesn’t see what happens in the template. So when Biome looks at this:
<script lang="ts"> let isOpen = $state(false); </script> <button onclick={() => isOpen = !isOpen}> Toggle </button>It only sees
let isOpen = $state(false)and thinks: “this variable is never reassigned, useconst.”It completely misses the
isOpen = !isOpenhappening in the template markup.If you run
biome check --write, it will automatically changelettoconstand break your app.The Biome team has acknowledged this as an explicit limitation of their partial Svelte support.
For now, the workaround is to either disable the
useConstrule for Svelte files or addbiome-ignorecomments where needed.What About ESLint?
The Svelte ESLint plugin community has proposed two new rules to handle this properly:
svelte/rune-prefer-let(which recommendsletfor rune declarations) and a Svelte-awaresvelte/prefer-constthat understands reactive declarations. These would give you proper linting without the false positives.My Take
Svelte 5’s runes are special and deserve their own way of handling variable declarations.
React hooks are different.
Svelte’s compiler rewrites your variable.
I like Svelte.
Please fix Biome.
Don’t make me use eslint.
/ Programming / svelte / Typescript / Biome / Linting
-
JavaScript Still Doesn't Have Types (And That's Probably Fine)
Here’s the thing about JavaScript and types: it doesn’t have them, and it probably won’t any time soon.
Back in 2022, there was a proposal to add TypeScript-like type syntax directly to JavaScript. The idea was being able to write type annotations without needing a separate compilation step. But the proposal stalled because the JavaScript community couldn’t reach consensus on implementation details.
The core concern? Performance. JavaScript is designed to be lightweight and fast, running everywhere from browsers to servers to IoT devices. Adding a type system directly into the language could slow things down, and that’s a tradeoff many aren’t willing to make.
So the industry has essentially accepted that if you want types in JavaScript, you use TypeScript. And honestly? That’s fine.
TypeScript: JavaScript’s Type System
TypeScript has become the de facto standard for typed JavaScript development. Here’s what it looks like:
// TypeScript Example let name: string = "John"; let age: number = 30; let isStudent: boolean = false; // Function with type annotations function greet(name: string): string { return `Hello, ${name}!`; } // Array with type annotation let numbers: number[] = [1, 2, 3]; // Object with type annotation let person: { name: string; age: number } = { name: "Alice", age: 25 };TypeScript compiles down to plain JavaScript, so you get the benefits of static type checking during development without any runtime overhead. The types literally disappear when your code runs.
The Python Parallel
You might be interested to know that the closest parallel to this JavaScript/TypeScript situation is actually Python.
Modern Python has types, but they’re not enforced by the language itself. Instead, you use third-party tools like mypy for static analysis and pydantic for runtime validation. There’s actually a whole ecosystem of libraries supporting types in Python in various ways, which can get a bit confusing.
Here’s how Python’s type annotations look:
# Python Example name: str = "John" age: int = 30 is_student: bool = False # Function with type annotations def greet(name: str) -> str: return f"Hello, {name}!" # List with type annotation numbers: list[int] = [1, 2, 3] # Dictionary with type annotation person: dict[str, int] = {"name": "Alice", "age": 25}Look familiar? The syntax is surprisingly similar to TypeScript. Both languages treat types as annotations that help developers and tools understand the code, but neither strictly enforces them at runtime (unless you add additional tooling).
What This Means for You
If you’re writing JavaScript, stop, and use TypeScript. It’s mature and widely adopted. Now also you can run TypeScript directly in some runtimes like Bun or Deno.
Type systems were originally omitted from many of these languages because the creators wanted to establish a low barrier to entry, making it significantly easier for people to adopt the language.
Additionally, computers at the time were much slower, and compiling code with rigorous type systems took a long time, so creators prioritized the speed of the development loop over strict safety.
However, with the power of modern computers, compilation speed is no longer a concern. Furthermore, the type systems themselves have improved significantly in efficiency and design.
Since performance is no longer an issue, the industry has shifted back toward using types to gain better structure and safety without the historical downsides.
/ Programming / Python / javascript / Typescript