Tree Transformations
Putout Built-in Transformations

🏨 Built-in transformations

JavaScript

remove unused variables
  function show() {
-     const message = 'hello';
      console.log('hello world');
  }
remove duplicates from logical expressions
-a && b && a
+a && b
remove unused for-of variables
-for (const {a, b} of c) {
+for (const {a} of c) {
    console.log(a);
}
remove unreferenced variables
-let a;
- a = 1;
let b;
b = 2;
console.log(b);
remove duplicate keys
const a = {
-   x: 'hello',
-   ...y,
    x: 'world',
    ...y,
}
remove duplicate case
switch (x) {
    case 5:
        console.log('hello');
        break;
-   case 5:
-       console.log('zz');
-       break;
}
remove unused private fields
  class Hello {
    #a = 5;
-   #b = 3;
    get() {
        return this.#a;
    };
}
remove unused expressions
  function show(error) {
-     showError;
  }
remove useless variables
-   function hi(a) {
-       const b = a;
    };
+   function hi(b) {
    };
remove useless new(why (opens in a new tab))
-new Error('something when wrong');
+Error('something when wrong');
remove useless constructor(why (opens in a new tab))
-const s = String('hello');
+const s = 'hello';
remove useless map
-const [str] = lines.map((line) => `hello ${line}`);
+const [line] = lines;
+const str = `hello ${line}`;
remove useless continue
-for (sign = decpt, i = 0; (sign /= 10) != 0; i++)
-    continue;
+for (sign = decpt, i = 0; (sign /= 10) != 0; i++);
remove useless operand
-a = a + b;
+a += b;
remove useless return
-module.exports.traverse = ({push}) => {
-    return {
-        ObjectExpression(path) {
-        }
-    }
-};
+module.exports.traverse = ({push}) => ({
+    ObjectExpression(path) {
+    }
+});
remove useless array constructor
-const a = Array(1, 2, 3);
+const a = [1, 2, 3];
remove useless conditions
-if (zone?.tooltipCallback) {
-    zone.tooltipCallback(e);
-}
+zone?.tooltipCallback(e);
remove useless type conversion
-const a = Boolean(b.includes(c));
+const a = b.includes(c);
 
--if (!!a)
++if (a)
    console.log('hi');
 
remove useless functions
-const f = (...a) => fn(...a);
-array.filter((a) => a);
 
+const f = fn;
+array.filter(Boolean);
remove useless typeof
- typeof typeof 'hello';
+ typeof 'hello';
declare undefined variables
const fs = import 'fs/promises';
const {stub} = import 'supertape';
 
+const {assign} = Object;
 
const readFile = stub();
 
assign(fs, {
    readFile,
});
remove useless arguments
onIfStatement({
    push,
-   generate,
-   abc,
})
 
function onIfStatement({push}) {
}
remove useless template expressions
-let y = `${"hello"} + ${"world"}`;
+let y = `hello + world`;
remove useless for-of
-for (const a of ['hello']) {
-    console.log(a);
-}
+console.log('hello');
remove useless array.entries() (opens in a new tab)
-for (const [, element] of array.entries()) {
-}
+for (const element of array) {
+}
reuse duplicateinit
const putout = require('putout');
-const {operator} = require('putout');
+const {operator} = putout;
convert assignment to arrow function
-const createRegExp = (a) = RegExp(a, 'g');
+const createRegExp = (a) => RegExp(a, 'g');
convert assignment to comparison
-if (a = 5) {
+if (a === 5) {
}
convert quotes to backticks
-const a = 'hello \'world\'';
+const a = `hello 'world'`;
convert typeof to is type
+const isFn = (a) => typeof a === 'function';
+
+if (isFn(fn))
-if (typeof fn === 'function')
    fn();
convert bitwise to logical
-a | !b
+a || !b
convert equal to strict equal
-if (a == b) {
+if (a === b) {
}
convert indexOf to includes
-if (~array.indexOf(element)) {
+if (array.includes(element)) {
}
remove useless escape
-const t = 'hello \"world\"';
-const s1 = `hello \"world\"`;
-const s = `hello \'world\'`;
+const t = 'hello "world"';
+const s1 = `hello "world"`;
+const s = `hello 'world'`;
remove useless Array.from
-for (const x of Array.from(y)) {}
+for (const x of y) {}
remove useless spread
-for (const x of [...y]) {}
+for (const x of y) {}
remove debugger statement
- debugger;
remove iife
-(function() {
-    console.log('hello world');
-}());
+console.log('hello world');
remove boolean from assertions
-if (a === true)
+if (a)
    alert();
remove boolean from logical expressions
-const t = true && false;
+const t = false;
remove nested blocks
for (const x of Object.keys(a)) {
-   {
-       console.log(x);
-   }
+   console.log(x);
}
remove unreachable code
function hi() {
    return 5;
-   console.log('hello');
}
split variable declarations
-let a, b;
+let a;
+let b;
split nested destructuring
-const {a: {b}} = c;
+const {a} = c;
+const {b} = a;
simplify assignment
-const {a} = {a: 5};
-const [b] = [5];
+const a = 5;
+const b = 5;
simplify logical expressions
-!(options && !options.bidirectional);
+!options || options.bidirectional;
simplify ternary
-module.exports = fs.copyFileSync ? fs.copyFileSync : copyFileSync;
+module.exports = fs.copyFileSync || copyFileSync;
remove console.log calls
-console.log('hello');
remove empty block statements
-if (x > 0) {
-}
remove empty patterns
-const {} = process;
remove strict mode directive from esm
-'use strict';
-
import * from fs;
Add strict mode directive in commonjs if absent
+'use strict';
+
const fs = require('fs');
remove constant conditions
function hi(a) {
-   if (2 < 3) {
-       console.log('hello');
-       console.log('world');
-   }
+   console.log('hello');
+   console.log('world');
};
 
function world(a) {
-   if (false) {
-       console.log('hello');
-       console.log('world');
-   }
};
convert esm to commonjs (disabled)
-import hello from 'world';
+const hello = require('world');
convert commonjs to esm (disabled)
-const hello = require('world');
+import hello from 'world';
convert replace to replaceAll (stage-4 (opens in a new tab))
-'hello'.replace(/hello/g, 'world');
+'hello'.replaceAll('hello', 'world');
apply destructuring
-const hello = world.hello;
-const a = b[0];
+const {hello} = world;
+const [a] = b;
apply await import
-const {readFile} = import('fs/promises');
+const {readFile} = await import('fs/promises');
apply if condition
-if (2 > 3);
+if (2 > 3)
    alert();
apply isArray (opens in a new tab)
-x instanceof Array;
+Array.isArray(x);
apply Array.at(not bundled (opens in a new tab))
-const latest = (a) => a[a.length - 1];
+const latest = (a) => a.at(-1);
apply numeric separators(proposal-numeric-separator (opens in a new tab))
-const a = 100000000;
+const a = 100_000_000;
apply optional chaining (proposal-optional-chaining (opens in a new tab))
-const result = hello && hello.world;
+const result = hello?.world;
apply nullish coalescing (proposal-nullish-coalescing (opens in a new tab), not bundled)
-result = typeof result  === 'undefined' ? 'hello': result;
result = result ?? 'hello';
convert throw statement into expression (proposal-throw-expressions (opens in a new tab), not bundled)
-const fn = (a) => {throw Error(a);}
+const fn = (a) => throw Error(a);
merge destructuring properties
-const {one} = require('numbers'):
-const {two} = require('numbers');
+ const {
+   one,
+   two
+} = require('numbers');
merge duplicate imports
-import {m as b} from 'y';
-import {z} from 'y';
-import x from 'y';
+import x, {m as b, z} from 'y';
merge if statements
-if (a > b)
-    if (b < c)
-        console.log('hi');
+if (a > b && b < c)
+    console.log('hi');
convert Math.pow to exponentiation operator
-Math.pow(2, 4);
+2 ** 4;
convert anonymous to arrow function
-module.exports = function(a, b) {
+module.exports = (a, b) => {
}
convert for to for-of
-for (let i = 0; i < items.length; i++) {
+for (const item of items) {
-   const item = items[i];
    log(item);
}
convert forEach to for-of
-Object.keys(json).forEach((name) => {
+for (const name of Object.keys(json)) {
    manage(name, json[name]);
-});
+}
convert for-in to for-of
-for (const name in object) {
-   if (object.hasOwnProperty(name)) {
+for (const name of Object.keys(object)) {
    console.log(a);
-   }
}
convert map to for-of
-names.map((name) => {
+for (const name of names) {
    alert(`hello ${name}`);
+}
-});
convert array copy to slice
-const places = [
-    ...items,
-];
+const places = items.slice();
extract sequence expressions
-module.exports.x = 1,
-module.exports.y = 2;
+module.exports.x = 1;
+module.exports.y = 2;
extract object properties into variable
-const {replace} = putout.operator;
-const {isIdentifier} = putout.types;
+const {operator, types} = putout;
+const {replace} = operator;
+const {isIdentifier} = types;
convert apply to spread
-console.log.apply(console, arguments);
+console.log(...arguments);
convert concat to flat
-[].concat(...array);
+array.flat();
convert arguments to rest
-function hello() {
-    console.log(arguments);
+function hello(...args) {
+    console.log(args);
}
convert Object.assign to merge spread
function merge(a) {
-   return Object.assign({}, a, {
-       hello: 'world'
-   });
+   return {
+       ...a,
+       hello: 'world'
+   };
};
convert comparison to boolean
-   const a = b === b;
+   const a = true;

Promises

remove useless await
-   await await Promise.resolve('hello');
+   await Promise.resolve('hello');
remove useless async
-const show = async () => {
+const show = () => {
    console.log('hello');
};
add missing await
-runCli();
+await runCli();
 
async function runCli() {
}
add await to return promise() statements (because it's faster, produces call stack and more readable (opens in a new tab))
async run () {
-   return promise();
+   return await promise();
}
apply top-level-await (proposal-top-level-await (opens in a new tab), enabled for ESM)
import fs from 'fs';
 
-(async () => {
-    const data = await fs.promises.readFile('hello.txt');
-})();
+const data = await fs.promises.readFile('hello.txt');
remove useless Promise.resolve
async () => {
-    return Promise.resolve('x');
+    return 'x';
}
convert Promise.reject to throw
async () => {
-    return Promise.reject('x');
+    throw 'x';
}

Node.js

convert fs.promises to fs/promises for node.js (opens in a new tab)
-const {readFile} = require('fs').promises;
+const {readFile} = require('fs/promises');
convert top-level return into process.exit()(because EcmaScript Modules doesn't support top level return)
-   return;
+   process.exit();
remove process.exit call
-process.exit();

Tape

replace test.only with test calls
-test.only('some test here', (t) => {
+test('some test here', (t) => {
    t.end();
});
replace test.skip with test calls
-test.skip('some test here', (t) => {
+test('some test here', (t) => {
    t.end();
});

TypeScript

remove duplicates from union
-type x = boolean[] | A | string | A | string[] | boolean[];
+type x = boolean[] | A | string | string[];
convert generic to shorthand(why (opens in a new tab))
interface A {
-    x: Array<X>;
+    x: X[];
}
remove useless types from constants
-const x: any = 5;
+const x = 5;
remove useless mapped types (opens in a new tab)
-type SuperType = {
-   [Key in keyof Type]: Type[Key]
-}
+type SuperType = Type;
remove useless mapping modifiers (opens in a new tab)
type SuperType = {
-   +readonly[Key in keyof Type]+?: Type[Key];
+   readonly[Key in keyof Type]?: Type[Key];
}
remove useless types
type oldType = number;
-type newType = oldType;
-const x: newType = 5;
+const x: oldType = 5;
remove duplicate interface keys
interface Hello {
-   'hello': any;
    'hello': string;
}
remove unused types
type n = number;
-type s = string;
const x: n = 5;
apply as type assertion (according to best practices (opens in a new tab))
-const boundaryElement = <HTMLElement>e.target;
+const boundaryElement1 = e.target as HTMLElement;
apply utility types (opens in a new tab)
-type SuperType = {
-    [Key in keyof Type]?: Type[Key];
-}
+type SuperType = Partial<Type>;