const migration = async () => {
// TODO: Fix TypeScript errors
return Promise.resolve(success);
0 / 14
~/handsontable-migration — zsh — 80x24
$ git status
On branch feature/typescript-2026
Your branch is ahead of 'origin/develop' by 1 commit.
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: src/core/Grid.ts
modified: src/plugins/index.ts
modified: package.json
deleted: src/legacy/*.js (30,000 lines)
$ npx tsc --noEmit
0 errors found in 342,000 lines of code
TypeScript compilation successful
All 6 framework wrappers migrated
... completed in 1.2s
Lines Changed
30,000+
Files Modified
487
Time Saved
~40 days

30,000 Lines Overnight

What Happens When You Let AI Rewrite Your Library in TypeScript

The morning I came to work and found a TypeScript Handsontable
1 / 14
The Problem That Wouldn't Get Solved
A JavaScript data grid library born in 2012 — the same year TypeScript was invented
STALLED
tokei ~/handsontable
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
LanguageFilesLinesCodeCommentsBlanks
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
JavaScript2,358481,853352,05353,63176,169
Markdown823245,1800162,30582,875
JSON102131,986131,975011
TypeScript98477,32466,9393,7526,633
TSX25122,05719,2128412,004
JSX21916,89615,1974861,213
CSS1015,3524,654247451
Sass544,9074,036180691
+ 8 more...
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Total5,2661,040,820642,198223,566175,056
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
JS: 481,853
TS+TSX: 99,381
Other: 459,586
Total Files
5,266
Total Lines
1,040,820
#TS-2024-001 OPEN
TypeScript Migration
Migrate the entire Handsontable codebase from JavaScript to TypeScript. A JavaScript data grid library born in 2012 — the same year TypeScript was invented. The task had been open for years.
Born: 2012
Status: Stalled
Attempts: Multiple
Always the same verdict: too expensive, too risky, too disruptive. The math never worked.
1b / 14
The Attempts That Didn't Work
Multiple tries, same result — until the economics changed
FAILED × 4
Attempt #1 Before Feb 2025
Manual + allowJS
Done before I joined the company. Internal developers tried migrating file by file with allowJS. Progress was slow, context switching killed momentum.
Result
Abandoned
Too slow, too disruptive
Attempt #2 2025 — On a train
YOLO on a Train
Traveling to a conference. Working in Cursor with Claude Sonnet 3.7. Cursor crashed. Lost context constantly. Made the same mistakes on loop.
Result
Got nowhere
Tools weren't ready yet
Attempt #3 2025 — Colleague
Systematic POC
Tools: Cursor (agent mode), Aider, Cline
Got 900 TS errors. Estimated 40+ man-days for full migration (splittable across team).
Based on converting a 5.54× smaller project — without running type-checker or fixing errors. Likely longer in practice.
Estimate
40+ days
Errors
900+
Report: impossible in one go
Attempt #4 JSNation contact
Contractor Quote
Met at JSNation. Said: "It's simple — just DeepSeek V3 and a good prompt."
After a PoC, the quote came back at $25,000 success fee, 30 days.
Quote
$25,000
The math never justified it
Every attempt hit the same wall: the codebase was too large, the risk too high, the ROI too uncertain.
allowJS Train YOLO 40-day POC $25k quote
2 / 14
2025
On the train to a conference — attempting the impossible
2 / 14
Two Trains. One Year Apart.
2025 — The failed attempt
2025 • FAILED
2025
Failed
On the train to conference
Working on TypeScript migration in Cursor with Claude Sonnet 3.7. Cursor crashed constantly, lost context, made same mistakes on loop.
Colleague's systematic POC
A careful, documented proof of concept. Got 900 TypeScript errors. Estimated 40+ man-days. Concluded: impossible in one go.
TS Errors
900+
Man-days
40+
Verdict
Impossible
// Cursor kept crashing
const migration = async () => {
  return "failed";
}
900 TypeScript
errors found
Too expensive. Too risky. Too disruptive.
2 / 14
2026
Same train. Same conference. The job was already done.
2 / 14
Two Trains. One Year Apart.
2026 — Same train, different world
2026 • SUCCESS
2026
Success
Same train, job done
Same train, same conference, same co-passenger. This time the job was already done. 30,000 lines migrated overnight.
TS Errors
0
Man-days
1
Cost
$1,000
// Migration complete
const migration = async () => {
  return "success";
}
0 TypeScript
errors. Clean.
30,000 lines migrated. Compiled. Done.
2e
The YOLO Prompt
No ticket. No sprint item. No plan. This is the key slide.
YOLO
11pm on a weeknight
Nobody told me to do this. I'd been hearing about a new model. The kind of thing you read about at 11pm and think: "I wonder if this actually works."
Full YOLO mode
One prompt. No strategy. No file-by-file discipline. Pointed it at the whole codebase. Left the machine on. Went home. (I don't even remember the exact prompt — it wasn't saved in my Cursor.)
The next morning
Came back expecting a mess. Found something that compiled. Spent a few more hours polishing it in Cursor. Then checked the billing. No spending limit was set. The ~$1,000 was the YOLO prompt + the polishing session combined.
One night. No spending limit.
~$1,000
Contractor: $25,000
POC: 30 days
Planned?
NO
On roadmap now?
YES
2f
The Boss Reaction
Was it planned? No. Did it work? Yes. Is it on the roadmap now? Absolutely.
APPROVED
Boss happy about the cost
Approved.
Showed the boss the result. Showed the cost.
~$1,000 instead of $25,000.
One night instead of 30 days.
2f2
Was it planned?
No.
2f3
Is it on the roadmap now?
YES.
2g
cursor — bash — 80x24
$ git checkout -b feature/yolo-typescript-migration
Switched to a new branch 'feature/yolo-typescript-migration'
$ cursor --mode=yolo --target=./src --prompt="Convert entire codebase to TypeScript"
Processing 342,000 lines of JavaScript...
Analyzing dependencies and types...
Generating TypeScript definitions...
Compilation successful with 0 errors
Nobody told me to do this. No ticket. No sprint item. No plan. Full YOLO mode.
CONTRACTOR QUOTE
$25,000
30 days, success fee
✗ Too expensive, too risky
OVERNIGHT COST
$1,000
One night, no planning
✓ It compiled!
SAVINGS
$24,000
96% cost reduction
✓ On roadmap now
Planned? NO
On roadmap now? YES
File changes: +30,000/-28,000
Spending limit set? NO
2h
2i
Industry Leaders Return to Coding with AI
Mark Zuckerberg is back to landing diffs — 20 years later
FOUNDERS × AI
Mark Zuckerberg 2003
2003 – 2006
The Coding Years
Built Facebook from scratch. Wrote a lot of code. Last meaningful contributions around 2006, when the codebase had fewer than 10,000 diffs.
Mark Zuckerberg 2024
2010 – 2025
The Silent Years
Last diff: made profile photos clickable. Blocked by an engineer for formatting issues. Zuck force-merged it. Then… silence for 15 years.
Mark Zuckerberg 2026
March 2026
Back to Coding
Landed 3 diffs using Claude Code CLI. One approved by 200+ engineers. Working on an unreleased greenfield Android app. Greenfield > legacy for AI-assisted coding.
Also: Garry Tan (Y Combinator CEO) is knee-deep in coding 15 years later. Founders with technical backgrounds being hands-on with AI agents could be a good thing — especially when the “honeymoon” period ends.

I'm an Engineering Manager
and I can “code” again !!!

3 / 14
TypeScript: Before and After LLMs
The shift from nice-to-have to necessary infrastructure
PARADIGM SHIFT
BEFORE
Nice to have
Developer Experience
Better DX, autocomplete, refactoring safety, self-documenting APIs. Good reasons, but never enough to justify the cost for a stable library.
Priority
Low
Impact
DX
Why it didn't happen
Too expensive, too risky, too disruptive. The math never worked for a stable library.
// JavaScript — ambiguity at every boundary
function process(data) {
  return data.map(x => x.value);
}
AFTER
Necessary
AI Collaboration Requirement
LLMs work significantly better with strongly typed codebases. Types are context, constraints, and communication.
Priority
Critical
Impact
AI
Types as a map
Dynamic JS gives AI ambiguity. TypeScript gives it a map. The migration that was "nice to have" quietly became necessary infrastructure.
// TypeScript — clear boundaries for AI
function process(data: DataItem[]): Result[] {
  return data.map(x => x.value);
}
4 / 14
Why the Architecture Actually Helped
Good architecture helps humans and AIs for the same reasons
ARCHITECTURE
Architecture
Microkernel
Plugins
40+
Custom ESLint Rules
Machine-readable arch docs
What the linter enforced
No raw window/document/console. No barrel imports. No cross-plugin direct calls. No native throw new Error().
Microkernel + Plugin Architecture
CORE KERNEL
AutoFill
Filters
Sort
Merge
Undo
Comments
Search
+33 more
Each plugin is a bounded context. The AI could reason about one plugin without holding 342k lines in mind. The linter didn't just catch bad code — it told the AI how to think about the codebase.
6 / 14
What "It Compiled" Actually Meant
AI solved the activation energy problem. Not the whole problem.
ANALYSIS
tsc --noEmit
0 errors
@ts-ignore
0 comments
any usages
~138
unknown usages
~2,088
Silicon Valley unexpected success reaction
TypeScript Migration Reality
any 138 object 79 as unknown 194
✓ Compilation successful
any usages
~138
object types
~79
as unknown as
~194
unknown
~2,088
// AI solved activation energy, not the whole problem
const migration = {
  status: "compiled",
  quality: "needs_work",
  next: "manual_cleanup"
}
7 / 14
The Dirty Truth About any
AI defaults to escape hatches under ambiguity
WARNING
Walkontable any
87 occurrences
Total any in codebase
~138
AI coverage
80%
Manual cleanup needed
20%
strict: true is still off. Enabling it is a 5-phase plan.
AI gives you 80% fast. The last 20% is still yours.
Walkontable Module
87 any
// AI-generated TypeScript with escape hatches
export class Walkontable {
  private _data: any[];
  private _options: any;

  constructor(data: any[], options: any) {
    this._data = data as any;
    this._options = options as unknown;
  }

  public getCell(row: number, col: number): any {
    return this._data[row][col] as any;
  }
}
Cleanup requires understanding actual types, not just annotating them
strict: true
OFF
Phase
1/5
Quality
80%
8 / 14
When Your .d.ts Files Are Lying
A TypeScript migration is also a reckoning with your own past promises
AUDIT
Hand-authored .d.ts files
235
Types vs implementation
Mismatch
Became
API audit
The TypeScript compiler became an API audit nobody asked for. And it found real problems. Types retrofitted over years — some didn't match the real implementation.
Silicon Valley denial/discovery moment
The Broken Contract
// What the .d.ts file promised
interface GridSettings {
  data: CellValue[][];
  columns?: ColumnSettings[];
  readOnly?: boolean;
}

// What the implementation actually did
function loadData(data) {
  // Accepts objects, arrays, functions...
  // Returns different types based on context
  // Has 3 undocumented optional params
  if (typeof data === "function") {
    return data.call(this, settings);
  }
}
235 .d.ts files — types retrofitted over years. Some didn't match the real implementation at all.
9 / 14
The PR That Reveals the Scale of Done
The scope of "done" expands once you start looking
PR #12011
Branch
feature/typescript-2026
Commits
23
Framework wrappers affected
6
Test files still in JS
~970
Spec files differ from develop
34
PR #12011 — February 2026
All 6 Framework Wrappers
React
Angular
Vue
Vue3
React-wrap
Angular-wrap
The PR is open. Cannot be merged. It's a breaking change.
// The reality of "done"
const pr = {
  number: 12011,
  status: "open",
  mergeable: false,
  reason: "breaking_change"
}
10 / 14
E2E Tests as the Source of Truth
Without a strong test suite you cannot trust an AI-generated migration
CONTRACT
Rule: all e2e tests must pass. Zero changes to test code allowed.
This constraint was the most important decision of the whole migration.
All E2E Tests Pass
No changes to test code. The tests prove the migration preserved behavior.
Zero Test Modifications
If the tests need changing, the migration is wrong — not the tests.
The Tests Are the Contract
They define what the library does. Everything else is implementation detail.
// The one rule that made everything possible
assert(e2eTests.allPassing() === true);
assert(e2eTests.modified() === 0);
11 / 14
The Hidden Cost: Framework Wrappers
The library is not the whole system. Plan for the ecosystem.
ECOSYSTEM
React
Interface mismatches found
Angular
Interface mismatches found
Vue
Interface mismatches found
DEPRECATED & REMOVED
Vue3
Interface mismatches found
React Wrapper
Human review needed
Angular Wrapper
Human review needed
TypeScript strictness exposed interface mismatches in all wrappers. Not automated. Each needed human review.
The library is not the whole system. Plan for the ecosystem — every wrapper is a surface you need to check.
12 / 14
The Merge Problem Nobody Warned You About
Timing and branching strategy matter as much as the migration itself
CONFLICT
Git Timeline
develop
Active development continues...
feature/typescript-2026
TypeScript migration branch
commit • commit • commit • commit
CONFLICT
Branches diverge further every day
Branch status
Cannot merge
Reason
Breaking change
The Ticking Clock
Development continued on develop during the entire migration. Every commit to develop is a potential conflict with the TS branch. The branch still cannot be merged. It's a breaking change.
// Every day the gap grows
const divergence = {
  develop: "moving forward",
  typescript: "waiting to merge",
  conflicts: "accumulating"
}
12b
Can this code be merged, sold, and shipped with IP?
Can it be merged?
AI-generated code in a commercial product. Who owns the output? What are the license implications?
Can it be sold?
Handsontable is a commercial library. Customers pay for it. Can AI-written code be part of a paid product?
IP ownership?
Does the AI provider have any claim? Is the output copyrightable? What do the ToS say?
Open question for the industry
The legal framework for AI-generated code in commercial products is still evolving. This is a conversation every team shipping AI-assisted code needs to have.
12c
Saul Goodman
We asked. They answered.
Our lawyers said:
It's fine.
We consulted our legal team about merging AI-generated code into a commercial product sold with IP protection. The answers came back clear.
Can be merged into the codebase
Can be sold as part of a commercial product
IP ownership stays with us
13 / 14
Lessons Learned
What I'd tell myself before doing this again
TAKEAWAYS
  • 1 AI removes activation energy, not effort
  • 2 Set a spending limit before you go to bed
  • 3 TypeScript is no longer just about DX — it's about AI readability
  • 4 Microkernel + plugins: good for humans, great for AI agents
  • 5 Custom linter rules are architecture docs — write them for the AI too
  • 6 Let your e2e suite be the contract
  • 7 any is technical debt with a friendly face
  • 8 .d.ts files are promises you may have quietly broken
  • 9 A migration branch vs an active codebase is a ticking clock
  • 10 AI-generated code in commercial products — ask your lawyers early
  • 11 Your library is not the whole system — plan for wrappers and the ecosystem
  • 12 Sometimes the most important bets are the ones you almost didn't make
The most important lesson for me?

I can “code” again.

13b
About me
Mateusz Wojczal
Engineering Manager at Handsontable
Founder of Gdansk meet.js TypeScript meetup
(now with Michał Michalczuk — the guy from the train)
I like burning tokens.
14 / 14

What's Next + Q&A

Branch exists. Compiles clean. Waiting for the right release window.
A library born before TypeScript is about to ship it natively. Full circle.
Thank you! — Questions?
Press F for fullscreen • Arrow keys to navigate