Help

JavaScript to TypeScript Converter: PropTypes to Interfaces, Types Added

A JavaScript to TypeScript converter saves the first, dull pass of a migration. This one reads your code with a parser, turns React PropTypes into interfaces, takes types from default values and adds the types it can work out, then hands back formatted TypeScript. It is a JS to TS converter for one file or snippet at a time, it never runs your code, and nothing is uploaded. What it cannot know, it leaves for you, and the sections below say exactly what that is.


How to convert JavaScript to TypeScript online

  1. Paste your code into JavaScript or JSX. The box reads Paste JavaScript or JSX here… until you do. The limit is 5,000,000 characters.
  2. Wait a moment. The tool converts as you type or paste, about 0.35 seconds after the last change, so there is no button to press.
  3. Read the result under the heading TypeScript, or TypeScript (TSX) when the code has JSX. It is shown as read-only highlighted code.
  4. Check the chips above the code. They say what was done, for example 2 interfaces written, 11 type annotations added or 2 class properties declared. If nothing needed a type, the line reads Nothing here needed a type, so the code was only reformatted.
  5. Select Copy to clipboard. The button changes to Copied.
  6. Under the converted code, Conversion options holds two checkboxes, React mode and Strict mode. Change either and the code converts again.

If the text is not valid JavaScript you see This is not valid JavaScript: syntax error at line N, column M. with the offending line and a caret. Code that already contains TypeScript syntax counts as invalid.

A worked example: a React component with PropTypes

This is the input, a small counter component with PropTypes and an untyped event handler:

import React, { useState } from 'react'; import PropTypes from 'prop-types'; function Counter({ label, start, onChange }) { const [count, setCount] = useState(start); const step = (n = 1) => setCount(count + n); return <button onClick={(e) => onChange(count)}>{label}: {count}</button>; } Counter.propTypes = { label: PropTypes.string.isRequired, start: PropTypes.number, onChange: PropTypes.func, }; export default Counter;

The converter writes this. React mode and Strict mode give the same result here, and the chips read 1 interface written and 3 type annotations added:

import React, { useState } from 'react'; interface CounterProps { label: string; start?: number; onChange?: (...args: any[]) => any; } function Counter({ label, start, onChange }: CounterProps) { const [count, setCount] = useState(start); const step = (n: number = 1) => setCount(count + n); return ( <button onClick={(e: React.MouseEvent<HTMLButtonElement>) => onChange(count)} > {label}: {count} </button> ); } export default Counter;

Three things happened. The PropTypes became CounterProps, with label required because of isRequired and the other two props optional. The default value n = 1 gave n: number = 1. The onClick handler on a <button> gave e the type React.MouseEvent<HTMLButtonElement>. The unused prop-types import was dropped, and Prettier laid the JSX out over several lines. Notice what did not change: useState(start) stays as written, because the converter does not guess a type from how a value is used.

How React PropTypes become a TypeScript interface

With React mode on, the converter writes an interface named <Name>Props from the component’s PropTypes, puts it above the component and types the props with it. It works for function components (declarations, arrows and memo-wrapped ones) and for classes with static propTypes or a Foo.propTypes = {...} assignment. A prop with .isRequired is required; any other prop gets ?. Comments above a prop move into the interface.

PropTypesTypeScript
string, numberstring, number
boolboolean
func(...args: any[]) => any
array, objectany[], Record<string, any>
node, elementReactNode, ReactElement
arrayOf(T), objectOf(T)T[], Record<string, T>
oneOf([...])a union of literals, such as 'sm' | 'lg'
oneOfType([...])a union of the listed types
shape({...}), exact({...})an object type
instanceOf(Foo)Foo

The propTypes statement is removed, and so is the prop-types import when nothing else uses it. A class component also gets a state interface from this.state = {...} and becomes Component<Props, State>. A class with no PropTypes gets a props interface built from the this.props.x it reads. A function component with no PropTypes gets a props interface only in Strict mode.

Which types it adds without PropTypes

What Strict mode adds

Strict mode is off by default. Turn it on to see every gap. It writes an explicit any where a type cannot be worked out: let x; becomes let x: any;, const list = [] becomes any[], const o = {} becomes Record<string, any>, and untyped parameters get : any. Destructured parameters get a type literal, and useState([]) and useState(null) become useState<any[]>([]) and useState<any>(null). With Strict off, those spots are simply left without a type. Turning it on makes the list of things left to fix easy to search for.

What the converter leaves alone

It reads one piece of code and guesses nothing. These cases are left as they are or become any:

CaseWhat you get
Callback parameters passed straight to a call, such as items.map((x) => ...)Left alone, so TypeScript types them from the receiving function
Types from another file, or a custom PropTypes validatorany. The validator itself is not kept
A forwardRef componentIts PropTypes are left untouched
JSDoc type commentsNot read
A type guessed from a parameter’s name or how it is usedNever done
Anything that needs other filesNo cross-file inference, so convert and check one file at a time
PropTypes with React mode offLeft alone

The output is a first pass, not a finished migration. Paste it into your project, run tsc, and tighten each any by hand. The converter does not rename files, so save a component as .tsx yourself.

Why it keeps your comments and layout

The code is read with @babel/parser, and each annotation is inserted into your original text as an edit, so comments and the shape of your code survive. Prettier then reformats the result, keeping your quote style, semicolons and indent width. The parsing, editing and formatting all run in a Web Worker in your browser. Your code is never uploaded and never run.

To tidy code before you convert it, use Code Formatter. To make sense of minified or obfuscated scripts first, use JS Deobfuscator.


Frequently asked questions

How do I convert JavaScript to TypeScript online?

Paste your JavaScript or JSX into the box labelled JavaScript or JSX. About a third of a second later the TypeScript appears under the heading TypeScript, or TypeScript (TSX) when the code contains JSX. There is no Convert button. Select Copy to clipboard to take the result. It works on the code you paste, so it suits a component, a module or a snippet rather than a whole repository.

Can it turn React PropTypes into a TypeScript interface?

Yes. With React mode on, each propTypes object becomes an interface named after the component, such as CounterProps. A prop marked isRequired is required and every other prop gets a question mark. The propTypes statement is removed, and so is the prop-types import when nothing else in the file uses it. It handles function components (declarations, arrows and memo-wrapped ones) and classes with static propTypes or Foo.propTypes.

Which PropTypes does it understand?

string, number, bool, func, array, object, node, element, arrayOf, objectOf, oneOf, oneOfType, shape, exact and instanceOf. They become string, number, boolean, (...args: any[]) => any, any[], Record<string, any>, ReactNode, ReactElement, T[], Record<string, T>, a union of literals, a union of types, an object type and the class itself. A custom validator function cannot be turned into a type, so that prop becomes any.

How do I convert JSX to TSX?

Paste the JSX component. When the code has JSX the result is headed TypeScript (TSX), and React mode switches on by itself, so props get an interface and handlers get event types. The tool does not rename files. Save the result with a .tsx extension in your project.

What do React mode and Strict mode do?

React mode, under Conversion options, turns PropTypes into interfaces and types event handlers, props and hooks. It is on by default when the code has JSX, react or prop-types, and with it off PropTypes are left alone. Strict mode is off by default. It writes an explicit any wherever a type cannot be worked out, instead of leaving the type out.

Why does the output still have any in it?

A type is only written when it can be read from the code itself. A custom PropTypes validator, or a prop type imported from another file, becomes any, and Strict mode adds an explicit any for untyped variables and parameters. The result is a first pass. Run it through tsc and replace the any types with the real ones.

Why wasn't a callback parameter typed?

A parameter passed straight to a call, such as x in items.map((x) => x.id), is left as it is on purpose, so TypeScript can still type it from the function that receives the callback.

Does it keep my comments and formatting?

Comments stay, because annotations are inserted into your original code as text edits rather than by rebuilding it. The result is then run through Prettier, which keeps your quote style, semicolons and indent width but may re-wrap long lines. Comments above a PropTypes entry move into the matching interface property.

What happens if my JavaScript has an error?

You see This is not valid JavaScript: syntax error at line N, column M. with the real numbers, followed by the offending line and a caret under the spot. Input that already contains TypeScript syntax counts as invalid, because this tool expects JavaScript. The limit is 5,000,000 characters, about 5 MB.

Is my code uploaded or run?

No. The code is parsed and rewritten in your browser, in a Web Worker, and is never sent to a server. It is also never executed, so a script with side effects is safe to paste.


Recommended

Helps keep the tools free. ✨

Images are for illustration only. As an Amazon Associate I earn from qualifying purchases.