惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

A
About on SuperTechFans
Cloudbric
Cloudbric
C
CERT Recently Published Vulnerability Notes
G
GRAHAM CLULEY
V
Vulnerabilities – Threatpost
C
Cisco Blogs
T
Tenable Blog
P
Privacy International News Feed
T
The Exploit Database - CXSecurity.com
I
Intezer
AWS News Blog
AWS News Blog
IT之家
IT之家
博客园 - 司徒正美
C
Cybersecurity and Infrastructure Security Agency CISA
博客园 - 【当耐特】
The Hacker News
The Hacker News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Spread Privacy
Spread Privacy
S
SegmentFault 最新的问题
博客园 - Franky
人人都是产品经理
人人都是产品经理
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
V
Visual Studio Blog
C
CXSECURITY Database RSS Feed - CXSecurity.com
H
Hacker News: Front Page
Latest news
Latest news
Scott Helme
Scott Helme
腾讯CDC
宝玉的分享
宝玉的分享
大猫的无限游戏
大猫的无限游戏
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
A
Arctic Wolf
S
Securelist
雷峰网
雷峰网
The GitHub Blog
The GitHub Blog
Project Zero
Project Zero
Google DeepMind News
Google DeepMind News
P
Palo Alto Networks Blog
F
Fortinet All Blogs
Schneier on Security
Schneier on Security
云风的 BLOG
云风的 BLOG
Security Archives - TechRepublic
Security Archives - TechRepublic
The Last Watchdog
The Last Watchdog
WordPress大学
WordPress大学
MongoDB | Blog
MongoDB | Blog
L
LINUX DO - 最新话题
S
Schneier on Security
NISL@THU
NISL@THU
Jina AI
Jina AI
M
MIT News - Artificial intelligence

Sanity.io

A Board Game agent built using Sanity Context and Vercel's AI SDK | Sanity Build a prototype with Claude Code that your whole team can edit | Sanity What’s New - May 2026 | Sanity I built a London pub guide with v0 and the Sanity MCP in six hours. Here's what I learned. | Sanity Build a conference concierge with Agent Context and Anthropic | Sanity Build a content-aware Telegram agent with Vercel AI SDK and Chat SDK | Sanity How I used Agent API to generate photos for my family’s recipes | Sanity What’s New April - 2026 | Sanity Better context, better matches: An AI love story (for dogs) | Sanity How to write for an agent | Sanity Content Agent, meet Slack: AI content operations in your workflow | Sanity Structure powers intelligence | Sanity Your agent needs better content. Here's how to give it. | Sanity How to serve content to agents (a field guide) | Sanity Sanity TypeGen GA: Automatic TypeScript types for content and GROQ | Sanity Sanity is now available on the Vercel Marketplace | Sanity The logo soup problem (and how to solve it) | Sanity Content Releases: From scattered updates to coordinated publishing | Sanity What's New - February 2026 | Sanity How we solved the agent memory problem | Sanity v0 Builder Challenge: The winners | Sanity Introducing: Sanity Agent Skills | Sanity Content Agent: Days of work in one conversation | Sanity Our Sanity Values | Sanity Open Source Pledge 2025: Stepping up when it matters | Sanity v0 builder challenge: $3000 in prizes | Sanity Why AI Breaks Without Structured Content Operations | Sanity What’s New January - 2026 | Sanity BFCM 2025: What teams built when infrastructure stopped being the problem | Sanity How AI shaped holiday shopping and what it means for content in 2026 | Sanity Sanity Studio v5: Embracing React 19 | Sanity You’ll need a CMS eventually. Let your agent set it up. | Sanity “You should never build a CMS” | Sanity AI Content Operations: A 30-Day Implementation Guide | Sanity What’s New December - 2025 | Sanity Scheduled Drafts: Stop manually publishing content at midnight | Sanity What’s New November - 2025 | Sanity Everything *[NYC] 2025 recap: A day of AI, Content Operations, and Culture | Sanity Clankers and content operations | Sanity Content Agent: AI that understands your structured content is here | Sanity Why design-driven content modeling creates technical debt, not velocity | Sanity What's New October - 2025 | Sanity From studio to inbox: How Kevin Green eliminated email campaign friction | Sanity The content editor's guide to content operations [E-commerce edition] | Sanity styled-components maintenance mode: A 40% faster fork | Sanity From zero code to a live website in 7 hours (thanks, Cursor!) | Sanity First attempt will be 95% garbage: A staff engineer's 6-week journey with Claude Code | Sanity Internationalization is more than translating words | Sanity What's New - September 2025 | Sanity We just deleted our 35k-member community Slack | Sanity What's New - August 2025 | Sanity The engineer's guide to content operations [E-commerce edition] | Sanity SEO for AI: Evolving from Web Pages to the Content Lake | Sanity What's New - July 2025 | Sanity Sanity Studio v4: A major version bump for a minor reason | Sanity What's New - June 2025 | Sanity Dashboard and Insights: Your New Content HQ | Sanity Canvas: AI-accelerated, context-aware, freeform authoring | Sanity Agent Actions: AI building blocks for structured content | Sanity Functions: Life beyond pressing publish | Sanity A new era for content applications with Sanity App SDK | Sanity The end of CMS era and our $85M Series C. | Sanity What's New – May 2025 | Sanity Introducing the Sanity Model Context Protocol (MCP) server | Sanity What's New – April 2025 | Sanity Pushing all the envelopes with ambitious content | Sanity Self-hosting is only free if your time is worth nothing | Sanity Content that lasts: Scaling beyond your frontend | Sanity The Live Content API is now Generally Available | Sanity The future beyond AI chat bots | Sanity Learning the new skill of working with AI | Sanity What's New - March 2025 | Sanity Give it in plain text: Making your content AI-Ready | Sanity No More 'DO NOT PUBLISH': Introducing Content Releases | Sanity React in 2025, what's next? | Sanity The final boss of front-end: block editors | Sanity Introducing Sanity for Startups | Sanity A block content editor that loves you back | Sanity A Black Friday Snooze Fest: Massive Traffic, No Drama | Sanity How to make a recipe site that scales well | Sanity The Sanity Winter Release 2024 | Sanity AVIF Arrives, Sanity’s Promise Fulfilled | Sanity Sanity joins the Open Source Pledge | Sanity Your content is now Live by default | Sanity Begin Team to Join Sanity | Sanity Sanity Digest - September '24 Edition | Sanity Sanity partners with Google. Now live on the Google Cloud Marketplace. | Sanity Sanity Digest - August ‘24 Edition | Sanity Now playing: the latest Mux Video Input plugin for Sanity | Sanity Community Digest - June ‘24 Edition | Sanity Community Digest - May ‘24 Edition | Sanity Guide to Sanity's newest product announcements | Sanity AI and Content Creation: A Leader's Guide | Sanity Of course, you should be able to type your content quickly! | Sanity New to AI Assist: translation, reference suggestions, image generation | Sanity Speak the language of your editors: Sanity Studio UI localization | Sanity Introducing the new Sanity Growth plan to serve collaborative teams | Sanity Presentation: Work faster than ever with structured content | Sanity Goodbye Feedback Frenzy, Hello Sanity Studio Comments! | Sanity Easing into the App Router with the Sanity Toolkit for Next.js | Sanity
Migrating the Sanity.io codebase from Flow to TypeScript | Sanity
Bjørge Næss · 2019-09-20 · via Sanity.io

We recently migrated the @sanity-packages written with Flow to TypeScript. It was an interesting journey and we'd like to share some experiences about how we ended up (re)configuring our build pipeline, how we dealt with CSS modules and how we managed to keep focus on compatibility in order to lay a solid foundation for the future of the Sanity.io codebase.

What is Flow? And what is TypeScript?

TypeScript and Flow both add support for static typing to JavaScript. Static typing means that the compiler (or even the code editor) will tell you about potential errors in your program, as opposed to dynamic typing where type errors are checked at execution time.

Why static types?

A statically typed language enables you as a developer to encode expectations about the structure of data types, variables, function arguments and return values in your code. A compiler will then tell you when these expectations are broken, thus giving early feedback about potential errors.

For example, it enables you to declare the structure of the props a component is expecting, and if you try to use a React component in a way that doesn't match these expectations, you will get feedback a lot earlier than you would have otherwise, (e.g. immediately as you write the code instead of when running it).

Below is the kind of feedback an editor can give you instantly as you type. Without static typing you would be relying on catching this error while testing or running the application, and noticing that your Hello component didn't work as expected.

A screenshot of an IDE showing a type error

Not only does this mean you get earlier feedback when developing code, but it also leads to safer, more robust and less error prone code, and increased maintainability.

A short history of the Sanity.io codebase

The first commit of what eventually became Sanity.io can be traced back to July 2014.

This doesn't mean that the oldest components haven't been modified since it was written. Quite the opposite. When we started out, this was the current state of Web development with React:

  • You made React components using React.createClass(), and you used mixins to solve shared concerns between components.
  • You compiled arrow functions and experimental JavaScript language features using the Traceur compiler (Babel wasn't even 6to5 yet).
  • For bundling you used browserify and a multitude of different *ify packages (for example reactify to get JSX support).
  • You needed Bluebird for Promise support.
  • You used JSHint (and JSXHint) for code linting.
  • Mocha, expect.js and sinon.js for testing.
  • And you didn't write CSS (duh!), you wrote Sass.
  • You wrote tiny packages which you worked on and published in isolation.

Staying ahead of tech has always been important to us, and over the years our codebase has been modernised gradually. A lot of great tools have eased this modernisation process. Most notably the codemod scripts made available by the React community have enabled us to migrate from React.createClass() to class components, etc.

As our codebase has been growing at a faster pace than our (still small) team of developers, we started experimenting with adding static typing to our codebase a couple of years ago. At the time, Flow, the static type checker developed by Facebook, seemed like a great choice because of its great support for React and seamless integration with our existing toolchain (Babel, ESLint, etc.), so we started using it for a few packages. It helped us a lot in thinking about types, but we also spent a considerable amount of time battling configuration and dealing with third party library typings.

Since then, TypeScript has matured a lot and even though Flow is still great and has also improved over the years, it now it feels like TypeScript has won. Also, our reasons for choosing Flow over TypeScript in the first place has been voided with the advent of Babel getting TypeScript support in v7, and with the recent TypeScript support in ESLint.

We now felt the time was ripe for getting rid of Flow.

Note: We're not getting into a deeper comparison of Flow vs TypeScript, as there are other and better resources for that.

The grunt work

Migrating from Flow to TypeScript was a bumpy journey, but a lot of great tools helped a lot, especially the flow-to-ts tool from Khan Academy. But even after running it, it took a lot of careful effort to make everything pass TypeScript's type checker. And then there were the realisations we had along the way...

Can't we just use Babel for everything?

We already had some TypeScript packages, for which we were using the TypeScript compiler. All our other packages were built using Babel.

We <3 Babel and have been using it since it was called 6to5. We have been using it to compile React/JSX, and it's enabled us to use new JavaScript language features before they were supported by every browser or runtime that we officially support. It also integrates well with ESLint, Webpack, and other tools we rely on, so getting rid of it would mean that we'd miss out on some of these features.

Still, maintaining two different build pipelines isn't exactly optimal, and Babel has had TypeScript support for a while already.

So to begin with we wanted to see if we could get rid of our existing TypeScript compile pipeline...

But no. Although Babel has TypeScript support, it only supports parsing TypeScript and compiling to plain JavaScript with type information omitted. A large part of the value of TypeScript comes from the ability to ship TypeScript Declaration files with your packages. Since we couldn't get that from Babel, getting rid of the TypeScript compiler was not an option.

Can't we just use the TypeScript compiler for everything, then?

Since TypeScript is a superset of JavaScript, all JavaScript code is also valid TypeScript. And the TypeScript compiler can be configured to both parse and do inference based type checking on JavaScript code. You can even generate type declarations from plain JavaScript. Here's a great writeup about that if you're interested.

Still, getting rid of Babel would mean getting rid of a lot of our existing tooling. A huge part of the reason is ESLint, which we still rely on for overall code quality and consistency.

Ok, so we're left with two compilers in any case. To minimise the maintenance burden, we then decided to use Babel for compiling all source code, while keeping the TypeScript compiler only for emitting type declaration files.

It was interesting to see how much more type errors TypeScript uncovered compared to Flow. The largest part of doing the migration was actually to fix semantic issues with our existing types.

Part imports

As mentioned earlier, the scope now was to get rid of Flow and improve/harmonise our TypeScript build pipeline. This meant not spending time on improvement of existing types, or adding types to new packages. A problem during this process was to figure out what to do with our part: imports, which is a special kind of module id that our plugin system is built upon. Due to TypeScript's support for declaring export types of modules, it was pretty straightforward to add declarations for just the part imports used in a package by making a d.ts file. Here's an example.

To begin with, all the exports are defined as the any type. The fact that TypeScript gives you the power to do this is great as it removes roadblocks during migration and then allows you to gradually refine the type system over time.

CSS Imports?

We use CSS modules for the Sanity Studio application, which means a few of our @sanity-packages import css files, e.g. import styles from './Component.module.css'. Since TypeScript has no support for understanding the syntax and inferring the exported types from CSS files, the stop-gap measure was to make a TypeScript Declaration file for all files ending with .css. Thanks to the wildcard support, this was just a matter of adding a file with the following:

Type-safe CSS Modules, still?

Although the above would make the TypeScript compiler happy when encountering CSS imports, it declares the export of all css files as any. However, another great discovery was typescript-plugin-css-modules. This is a plugin for the TypeScript Language Service, which is a separate compiler service optimised for incremental compilation and is typically what Code editors and IDEs use to provide immediate feedback.

This means that in editors that are using the TypeScript Language Service, you will still get type feedback when using CSS-modules:

A screenshot of an IDE providing type error on CSS modules

What does all this mean for you as a Sanity Developer?

Answer: Not much (yet)! The scope of this round of work was to improve our TypeScript pipeline and to get rid of Flow. No matter how tempting it was to fix the issues uncovered by TypeScript, it was important for us to stay clear of changing any semantics of the current code, and only work on type annotations. For this reason, there were several places we had to add // @ts-ignore comments to make the TypeScript compiler pass.

Writing this hurts, but it's justifiable in a transition period 🙈

These changes lay the foundation for gradually improving typings, adding static typing to existing packages. Eventually this work will surface to our users and come with several benefits:

  • Static types will help us catch a lot more errors before shipping updates.
  • It will enable our team of developers to ship code faster and more confidently.
  • It will make it a lot faster to refactor code and do cross-cutting changes without accidentally breaking stuff.
  • It will make our codebase more accessible to both new hires and external contributors.
  • It will help us in knowing what constitutes our public API, and what the community relies on, and therefore reduce our potential for accidentally breaking stuff.

These will in time lead to Sanity.io becoming an even better product.

Moving forward

As we move forward we will slowly but steadily be working on improving our type system. There are some interesting challenges ahead of us, for example how we can utilise static typing for document types defined by a sanity schema, and how we can provide type safe integration points for developers customising and doing integration work with Sanity.

Note: Although we will probably write more TypeScript in the future, we will not require Sanity developers to learn TypeScript in order to write plugins or integrate. TypeScript is completely optional and plain JavaScript will always work.

Our work has just begun in this area. Our long term goal is to provide TypeScript definitions for all our packages, and gradually improve our type system over time.

*) The Sanity codebase consists of a lot more than what's found in the public and Open Source sanity-io/sanity repository. This blog post is focused only on the public Open Source monorepo where we keep most of the JavaScript packages that developers interact with, and and editors rely on when using the Sanity Studio.