Skip to content

Latest commit

 

History

History
997 lines (728 loc) · 14.2 KB

refactoring.md

File metadata and controls

997 lines (728 loc) · 14.2 KB

Refactoring & Quick fixes

Availability

Refactorings

Refactorings are supported as of Visual Studio 2017 version 15.4.0.

TS Version Effects
2.4.0 Convert to ES6 class
2.5.0 Extract function
2.6.0 Extract constant
2.6.1 Annotate with type from JSDoc
2.6.1 Install @types for JS module
TBD Convert to default import

Code Fixes

Code fixes are supported as of Visual Studio 2017 version 15.0.0.

TS Version Error Codes Effects
2.1.4 2304, 2503, 2552, 2686 Add an appropriate import (?)
2.2.1 2339, 2551 Add missing member
2.2.1 2377 Synthesize missing super() call
2.2.1 2420, 2720 Implement interface members
2.2.1 2515, 2653 Implement abstract members from base class
2.2.1 2662, 2663 Prepend this. to member access
2.2.1 2689 Change extends to implements
2.2.1 17009 Move super() call ahead of this access
2.3.0 All Suppress JS diagnostic
2.4.0 2551, 2552 Correct misspelled name
2.4.1 6133, 6138 Handle unused symbol
2.5.0 2713 Rewrite as indexed access type
2.5.0 8020 Convert JSDoc type to TS type
2.6.1 1329 Call decorator factory expression
2.6.1 7005, 7006, 7008, 7010, 7019, 7032, 7033, 7034 Annotate with inferred type
2.6.1 2307, 7016 Install @types for JS module
2.7.0 1219 Set missing experimentalDecorators option
2.7.0 1308 Add async modifier to function containing await
2.9.0 2724 Fix spelling of imported module
2.9.0 7027 Remove unreachable code
2.9.0 80005 Convert require call to import
2.9.1 1340 Add missing typeof
2.9.1 7028 Remove unused label
3.0.0 1337 Convert to mapped object type
3.2.1 2348 Add missing new operator
3.2.1 2352 Add convert to unknown for non-overlapping types
3.2.1 7051 Add name to nameless parameter

Examples

Refactorings

Convert to ES6 Class

Before

function cls() {
    this.x = 10;
}
cls.prototype.method = function () {
    return 3;
}

After

class cls
{
    constructor()
    {
        this.x = 10;
    }
    method()
    {
        return 3;
    }
}

Notes

  • Only applies in JavaScript files.
  • The caret must be on an occurrence of the class name.

Extract Function

Before

function F(x: number)
{
    /*start*/return x + 1;/*end*/
}

After - Nested Function

function F(x: number)
{
    return newFunction();

    function newFunction()
    {
        return x + 1;
    }
}

After - Top-Level Function

function F(x: number)
{
    return newFunction(x);
}
function newFunction(x: number)
{
    return x + 1;
}

Notes

  • Extraction is best-effort - the resulting code may not compile or may behave subtly differently.
  • After the refactoring, the caret should be at the beginning of the call to the extracted function.

Extract Constant

Before

const PI = 3.141;
class Circle {
    readonly radius = 3;

    Area() {
        return /*start*/PI * (this.radius ** 2)/*end*/;
    }
}

After - Local

const PI = 3.141;
class Circle {
    readonly radius = 3;

    Area() {
        const newLocal = PI * (this.radius ** 2);

        return newLocal;
    }
}

After - Property

const PI = 3.141;
class Circle {
    readonly radius = 3;

    private readonly newProperty = PI * (this.radius ** 2);

    Area() {
        return this.newProperty;
    }
}

Notes

  • The extracted range must be an expression (exception
  • Extraction is best-effort - the resulting code may not compile or may behave subtly differently. In particular, evaluation order may change.
  • After the refactoring, the caret should be at the beginning of the call to the extracted constant/property.

Annotate with Type from JSDoc

Before

/** @type {number} */
var x;

After

/** @type {number} */
var x: number;

Notes

  • The caret must be on the name of the declaration to be annotated.

Install @types for JS Module

Before

import "left-pad";

After

import "left-pad";

Notes

  • The caret must be on the name of module being imported.
  • No change is made to the source. Instead, the corresponding @types module for the module being imported is installed in the project.
  • The refactoring will not be offered if no corresponding @types module is available.
  • If there is an error, the corresponding code fix will be offered instead.

Convert to Default Import

Before

// @Filename: /a.d.ts
declare const x: number;
export = x;
// @Filename: /b.ts
import * as a from "./a";
// @Filename: /c.ts
import a = require("./a");

After

// @Filename: /a.d.ts
declare const x: number;
export = x;
// @Filename: /b.ts
import a from "./a";
// @Filename: /c.ts
import a from "./a";

Notes

  • Currently this will only activate if --allowSyntheticDefaultImports is enabled.
  • The caret must be on the name of the module being imported.

Code Fixes

Add Missing Member

Before - TS2339

class C {
}

const c = new C();
c.P = 1; // TS2339

After - Property

class C {
    P: number;
}

const c = new C();
c.P = 1;

After - Index Signature

class C {
    [x: string]: number;
}

const c = new C();
c.P = 1;

Before - TS2551

class C {
    Prop1: number;
}

const c = new C();
c.Prop2 = 1; // TS2551

After - Property

class C {
    Prop2: number;
    Prop1: number;
}

const c = new C();
c.Prop2 = 1;

After - Index Signature

class C {
    [x: string]: number;
    Prop1: number;
}

const c = new C();
c.Prop2 = 1;

Notes

  • The receiver must have a class type.

Synthesize Missing super() Call

Before - TS2377

class Base {
}

class Derived extends Base {
    constructor() { //TS2377
    }
}

After

class Base {
}

class Derived extends Base {
    constructor() {
        super();
    }
}

Notes

  • Always calls super() without arguments.

Implement Interface Members

Before - TS2420

interface I {
    X: number
}

class C implements I { //TS2420
}

After

interface I {
    X: number
}

class C implements I {
    X: number;
}

Implement Abstract members from Base Class

Before - TS2515

abstract class A {
    abstract M();
}

class C extends A { //TS2515
}

After

abstract class A {
    abstract M();
}

class C extends A {
    M() {
        throw new Error("Method not implemented.");
    }
}

Before - TS2563

abstract class A {
    abstract M();
}

const c = class C extends A { //TS2563
}

After

abstract class A {
    abstract M();
}

const c = class C extends A {
    M() {
        throw new Error("Method not implemented.");
    }
}

Prepend this. to Member Access

Before - TS2662

  • For static members
class C {
    static m() { C.m(); }
    n() { m(); }
}

After

class C {
    static m() { C.m(); }
    n() { C.m(); }
}

Before - TS2663

class C {
    private x: number;
    Increment() {
        x++; //TS2663
    }
}

After

class C {
    private x: number;
    Increment() {
        this.x++;
    }
}

Change extends to implements

Before - TS2689

interface I {
}

class C extends I { //TS2689
}

After

interface I {
}

class C implements I {
}

Move super() Call Ahead of this Access

Before - TS17009

class Base {
}

class Derived extends Base {
    private x: number;
    constructor() {
        this.x = 1; //TS17009
        super();
    }
}

After

class Base {
}

class Derived extends Base {
    private x: number;
    constructor() {
        super();
        this.x = 1;
    }
}

Suppress JS Diagnostic

Before

// @ts-check

x++;

After - Suppress Single

// @ts-check

// @ts-ignore
x++;

After - Suppress File

// @ts-nocheck


x++;

Correct Misspelled Name

Before - TS2551

class C {
    Prop1: number;
}

const c = new C();
c.Prop2 = 1; // TS2551

After

class C {
    Prop1: number;
}

const c = new C();
c.Prop1 = 1;

Before - TS2552

let variable1 = 1;
variable2++; //TS2552

After

let variable1 = 1;
variable1++;

Notes

  • There are restrictions on what counts as a misspelling - the lengths must match and be greater than 3, etc.

Handle Unused Symbol

Before - TS6133

// { "compilerOptions": { "noUnusedParameters": true } }
function F(x: number) { //TS6133
}

After - Remove Declaration

// { "compilerOptions": { "noUnusedParameters": true } }
function F() {
}

After - Prepend Underscore

// { "compilerOptions": { "noUnusedParameters": true } }
function F(_x: number) {
}

Before - TS6138

// { "compilerOptions": { "noUnusedLocals": true } }
class C {
    constructor(private x: number) { //TS6138
    }
}

After - Remove Declaration

// { "compilerOptions": { "noUnusedLocals": true } }
class C {
    constructor() {
    }
}

After - Prepend Underscore

// { "compilerOptions": { "noUnusedLocals": true } }
class C {
    constructor(private _x: number) {
    }
}

Notes

  • Prepending an underscore to a constructor parameter declaring a property doesn't fix the error.

Rewrite as Indexed Access Type

Before - TS2713

interface I {
    x: number;
}

let z: I.x; //TS2713

After

interface I {
    x: number;
}

let z: I["x"];

Notes

  • Presently, doesn't work for classes.

Convert JSDoc Type to TS Type

Before - TS8020

// { "compilerOptions": {"strictNullChecks": true} }

let x: ?number; //TS8020

After - null

// { "compilerOptions": {"strictNullChecks": true} }

let x: number | null;

After - null, undefined

// { "compilerOptions": {"strictNullChecks": true} }

let x: number | null | undefined;

Notes

  • Not offered in JS files.

Call Decorator Factory Expression

Before - TS1329

// { "compilerOptions": {"experimentalDecorators": true, "target": "es5"} }

function DecoratorFactory() {
    return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
    }
}

class C {
    @DecoratorFactory //TS1329
    M() { }
}

After

// { "compilerOptions": {"experimentalDecorators": true, "target": "es5"} }

function DecoratorFactory() {
    return function (target, propertyKey: string, descriptor: PropertyDescriptor) {
    }
}

class C {
    @DecoratorFactory()
    M() { }
}

Set Missing experimentalDecorators Option

Before - TS1219

// @Filename: /dir/a.ts
declare const decorator: any;

class A {
    @decorator method() {};
};
// @Filename: /dir/tsconfig.json
{
    "compilerOptions": {
    }
}

After

// @Filename: /dir/a.ts
declare const decorator: any;

class A {
    @decorator method() {};
};
// @Filename: /dir/tsconfig.json
{
    "compilerOptions": {
        "experimentalDecorators": true,
    }
}

Notes

  • Experimental support for decorators is a feature that is subject to change in a future release.

Add async Modifier to Function Containing await

Before - TS1308

function f() {
    await Promise.resolve();
}

After

async function f() {
    await Promise.resolve();
}

Fix Spelling of Imported Module

Before - TS2724

// @Filename: file1.ts
export const fooooooooo = 1;
// @Filename: file2.ts
import {fooooooooa} from "./file1"; fooooooooa;

After

// @Filename: file1.ts
export const fooooooooo = 1;
// @Filename: file2.ts
import {fooooooooo} from "./file1"; fooooooooa;

Remove Unreachable Code

Before - TS7027

function f() {
    return 1;
    return 2;
}

After

function f() {
    return 1;
}

Convert require Call to import

Before - TS80005

const a = require("a");

After

import a = require("a");

Add Missing typeof

Before - TS1340

 declare module "foo" {
     const a = "foo"
     export = a
 }
 const x: import("foo") = import("foo");

After

 declare module "foo" {
     const a = "foo"
     export = a
 }
 const x: typeof import("foo") = import("foo");

Remove Unused Label

Before - TS7028

label1: while (1) {}

After

while (1) {}

Convert to Mapped Object Type

Before - TS1337

 type K = "foo" | "bar";
 interface SomeType {
     a: string;
     [prop: K]: any;
 }

After

 type K = "foo" | "bar";
 type SomeType = {
    [prop in K]: any;
} & {
    a: string;
};

Add Missing New Operator

Before - TS2348

class C {
}
var c = C();

After

class C {
}
var c = new C();

Add Convert to unknown for Non-Overlapping Types

Before - TS2352

const s1 = 1 as string;
const o1 = s + " word" as object;

const s2 = <string>2;
const o2 = <object>s2;

After

const s1 = 1 as unknown as string;
const o1 = s + " word" as unknown as object;

const s2 = <string><unknown>2;
const o2 = <object><unknown>s2;

Add Name to Nameless Parameter

Before - TS7051

var x: (number) => string;

After

var x: (arg0: number) => string;