add refactor of extract type (#30562)

* add basically implement

* add rename location and add testcase

* collection type arguments

* disallow infer type

* add support for typedef convert

* refactor info to make type safe

* disallow type pred

* avoid unnecessary branch

* disallow type query

* haha😂

Co-Authored-By: Kingwl <kingwenlu@gmail.com>

* Update src/services/refactors/extractType.ts

Co-Authored-By: Kingwl <kingwenlu@gmail.com>

* Update src/services/refactors/extractType.ts

Co-Authored-By: Kingwl <kingwenlu@gmail.com>

* add more tests

* add template tag support in jsdoc

* add support of type parameters constraint

* add more tests

* merge branch

* add more tests

* refactor and update function name
This commit is contained in:
Wenlu Wang
2019-05-07 08:26:53 -07:00
committed by Nathan Shively-Sanders
parent 6c4876a00a
commit 714821fc97
68 changed files with 1065 additions and 0 deletions
+13
View File
@@ -4962,6 +4962,19 @@
"category": "Message",
"code": 95076
},
"Extract type": {
"category": "Message",
"code": 95077
},
"Extract to type alias": {
"category": "Message",
"code": 95078
},
"Extract to typedef": {
"category": "Message",
"code": 95079
},
"No value exists in scope for the shorthand property '{0}'. Either declare one or provide an initializer." :{
"category": "Error",
"code": 18004
+147
View File
@@ -0,0 +1,147 @@
/* @internal */
namespace ts.refactor {
const refactorName = "Extract type";
const extractToTypeAlias = "Extract to type alias";
const extractToTypeDef = "Extract to typedef";
registerRefactor(refactorName, {
getAvailableActions(context): ReadonlyArray<ApplicableRefactorInfo> {
const info = getRangeToExtract(context);
if (!info) return emptyArray;
return [{
name: refactorName,
description: getLocaleSpecificMessage(Diagnostics.Extract_type),
actions: [info.isJS ? {
name: extractToTypeDef, description: getLocaleSpecificMessage(Diagnostics.Extract_to_typedef)
} : {
name: extractToTypeAlias, description: getLocaleSpecificMessage(Diagnostics.Extract_to_type_alias)
}]
}];
},
getEditsForAction(context, actionName): RefactorEditInfo {
Debug.assert(actionName === extractToTypeAlias || actionName === extractToTypeDef);
const { file } = context;
const info = Debug.assertDefined(getRangeToExtract(context));
Debug.assert(actionName === extractToTypeAlias && !info.isJS || actionName === extractToTypeDef && info.isJS);
const name = getUniqueName("NewType", file);
const edits = textChanges.ChangeTracker.with(context, changes => info.isJS ?
doTypedefChange(changes, file, name, info.firstStatement, info.selection, info.typeParameters) :
doTypeAliasChange(changes, file, name, info.firstStatement, info.selection, info.typeParameters));
const renameFilename = file.fileName;
const renameLocation = getRenameLocation(edits, renameFilename, name, /*preferLastLocation*/ false);
return { edits, renameFilename, renameLocation };
}
});
interface Info { isJS: boolean; selection: TypeNode; firstStatement: Statement; typeParameters: ReadonlyArray<TypeParameterDeclaration>; }
function getRangeToExtract(context: RefactorContext): Info | undefined {
const { file, startPosition } = context;
const isJS = isSourceFileJS(file);
const current = getTokenAtPosition(file, startPosition);
const range = createTextRangeFromSpan(getRefactorContextSpan(context));
const selection = findAncestor(current, (node => node.parent && rangeContainsSkipTrivia(range, node, file) && !rangeContainsSkipTrivia(range, node.parent, file)));
if (!selection || !isTypeNode(selection)) return undefined;
const checker = context.program.getTypeChecker();
const firstStatement = Debug.assertDefined(isJS ? findAncestor(selection, isStatementAndHasJSDoc) : findAncestor(selection, isStatement));
const typeParameters = collectTypeParameters(checker, selection, firstStatement, file);
if (!typeParameters) return undefined;
return { isJS, selection, firstStatement, typeParameters };
}
function isStatementAndHasJSDoc(n: Node): n is (Statement & HasJSDoc) {
return isStatement(n) && hasJSDocNodes(n);
}
function rangeContainsSkipTrivia(r1: TextRange, node: Node, file: SourceFile): boolean {
return rangeContainsStartEnd(r1, skipTrivia(file.text, node.pos), node.end);
}
function collectTypeParameters(checker: TypeChecker, selection: TypeNode, statement: Statement, file: SourceFile): TypeParameterDeclaration[] | undefined {
const result: TypeParameterDeclaration[] = [];
return visitor(selection) ? undefined : result;
function visitor(node: Node): true | undefined {
if (isTypeReferenceNode(node)) {
if (isIdentifier(node.typeName)) {
const symbol = checker.resolveName(node.typeName.text, node.typeName, SymbolFlags.TypeParameter, /* excludeGlobals */ true);
if (symbol) {
const declaration = cast(first(symbol.declarations), isTypeParameterDeclaration);
if (rangeContainsSkipTrivia(statement, declaration, file) && !rangeContainsSkipTrivia(selection, declaration, file)) {
result.push(declaration);
}
}
}
}
else if (isInferTypeNode(node)) {
const conditionalTypeNode = findAncestor(node, n => isConditionalTypeNode(n) && rangeContainsSkipTrivia(n.extendsType, node, file));
if (!conditionalTypeNode || !rangeContainsSkipTrivia(selection, conditionalTypeNode, file)) {
return true;
}
}
else if ((isTypePredicateNode(node) || isThisTypeNode(node))) {
const functionLikeNode = findAncestor(node.parent, isFunctionLike);
if (functionLikeNode && functionLikeNode.type && rangeContainsSkipTrivia(functionLikeNode.type, node, file) && !rangeContainsSkipTrivia(selection, functionLikeNode, file)) {
return true;
}
}
else if (isTypeQueryNode(node)) {
if (isIdentifier(node.exprName)) {
const symbol = checker.resolveName(node.exprName.text, node.exprName, SymbolFlags.Value, /* excludeGlobals */ false);
if (symbol && rangeContainsSkipTrivia(statement, symbol.valueDeclaration, file) && !rangeContainsSkipTrivia(selection, symbol.valueDeclaration, file)) {
return true;
}
}
else {
if (isThisIdentifier(node.exprName.left) && !rangeContainsSkipTrivia(selection, node.parent, file)) {
return true;
}
}
}
return forEachChild(node, visitor);
}
}
function doTypeAliasChange(changes: textChanges.ChangeTracker, file: SourceFile, name: string, firstStatement: Statement, selection: TypeNode, typeParameters: ReadonlyArray<TypeParameterDeclaration>) {
const newTypeNode = createTypeAliasDeclaration(
/* decorators */ undefined,
/* modifiers */ undefined,
name,
typeParameters.map(id => updateTypeParameterDeclaration(id, id.name, id.constraint, /* defaultType */ undefined)),
selection
);
changes.insertNodeBefore(file, firstStatement, newTypeNode, /* blankLineBetween */ true);
changes.replaceNode(file, selection, createTypeReferenceNode(name, typeParameters.map(id => createTypeReferenceNode(id.name, /* typeArguments */ undefined))));
}
function doTypedefChange(changes: textChanges.ChangeTracker, file: SourceFile, name: string, firstStatement: Statement, selection: TypeNode, typeParameters: ReadonlyArray<TypeParameterDeclaration>) {
const node = <JSDocTypedefTag>createNode(SyntaxKind.JSDocTypedefTag);
node.tagName = createIdentifier("typedef"); // TODO: jsdoc factory https://github.com/Microsoft/TypeScript/pull/29539
node.fullName = createIdentifier(name);
node.name = node.fullName;
node.typeExpression = createJSDocTypeExpression(selection);
const templates: JSDocTemplateTag[] = [];
forEach(typeParameters, typeParameter => {
const constraint = getEffectiveConstraintOfTypeParameter(typeParameter);
const template = <JSDocTemplateTag>createNode(SyntaxKind.JSDocTemplateTag);
template.tagName = createIdentifier("template");
template.constraint = constraint && cast(constraint, isJSDocTypeExpression);
const parameter = <TypeParameterDeclaration>createNode(SyntaxKind.TypeParameter);
parameter.name = typeParameter.name;
template.typeParameters = createNodeArray([parameter]);
templates.push(template);
});
changes.insertNodeBefore(file, firstStatement, createJSDocComment(/* comment */ undefined, createNodeArray(concatenate<JSDocTag>(templates, [node]))), /* blankLineBetween */ true);
changes.replaceNode(file, selection, createTypeReferenceNode(name, typeParameters.map(id => createTypeReferenceNode(id.name, /* typeArguments */ undefined))));
}
}
+1
View File
@@ -80,6 +80,7 @@
"refactors/convertExport.ts",
"refactors/convertImport.ts",
"refactors/extractSymbol.ts",
"refactors/extractType.ts",
"refactors/generateGetAccessorAndSetAccessor.ts",
"refactors/moveToNewFile.ts",
"refactors/addOrRemoveBracesToArrowFunction.ts",
@@ -0,0 +1,16 @@
/// <reference path='fourslash.ts' />
//// var x: /*a*/{ a?: number, b?: string }/*b*/ = { };
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = {
a?: number;
b?: string;
};
var x: NewType = { };`,
});
@@ -0,0 +1,17 @@
/// <reference path='fourslash.ts' />
//// function foo(a: number, b?: number, ...c: number[]): /*a*/boolean/*b*/ {
//// return false as boolean
//// }
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = boolean;
function foo(a: number, b?: number, ...c: number[]): NewType {
return false as boolean
}`,
});
@@ -0,0 +1,17 @@
/// <reference path='fourslash.ts' />
//// function foo(a: number, b?: number, ...c: number[]): boolean {
//// return false as /*a*/boolean/*b*/
//// }
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `function foo(a: number, b?: number, ...c: number[]): boolean {
type /*RENAME*/NewType = boolean;
return false as NewType
}`,
});
@@ -0,0 +1,21 @@
/// <reference path='fourslash.ts' />
//// interface A<T = /*a*/string/*b*/> {
//// a: boolean
//// b: number
//// c: T
//// }
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = string;
interface A<T = NewType> {
a: boolean
b: number
c: T
}`,
});
@@ -0,0 +1,21 @@
/// <reference path='fourslash.ts' />
//// interface A<T = string> {
//// a: /*a*/boolean/*b*/
//// b: number
//// c: T
//// }
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = boolean;
interface A<T = string> {
a: NewType
b: number
c: T
}`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type A<T = /*a*/boolean/*b*/> = string | number | T
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = boolean;
type A<T = NewType> = string | number | T`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type A<T = boolean> = /*a*/string/*b*/ | number | T
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = string;
type A<T = boolean> = NewType | number | T`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// var x: { a?: /*a*/number/*b*/, b?: string } = { };
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = number;
var x: { a?: NewType, b?: string } = { };`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type A<T = boolean> = string | number | /*a*/T/*b*/
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType<T> = T;
type A<T = boolean> = string | number | NewType<T>`,
});
@@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
//// type A<B, C, D = B> = /*a*/Partial<C | string>/*b*/ & D | C
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType<C> = Partial<C | string>;
type A<B, C, D = B> = NewType<C> & D | C`,
});
@@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
//// type A<B, C, D = B> = /*a*/Partial<C | string | D>/*b*/ & D | C
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType<C, D> = Partial<C | string | D>;
type A<B, C, D = B> = NewType<C, D> & D | C`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// var x: /*a*/string/*b*/ = '';
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = string;
var x: NewType = '';`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type A<T, U> = () => <T>(v: /*a*/T/*b*/) => (v: T) => <T>(v: T) => U
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType<T> = T;
type A<T, U> = () => <T>(v: NewType<T>) => (v: T) => <T>(v: T) => U`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type A<T, U> = () => <T>(v: T) => (v: /*a*/T/*b*/) => <T>(v: T) => U
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType<T> = T;
type A<T, U> = () => <T>(v: T) => (v: NewType<T>) => <T>(v: T) => U`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type A<T, U> = () => <T>(v: T) => (v: T) => <T>(v: /*a*/T/*b*/) => U
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType<T> = T;
type A<T, U> = () => <T>(v: T) => (v: T) => <T>(v: NewType<T>) => U`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type A<T, U> = () => <T>(v: T) => (v: T) => <T>(v: T) => /*a*/U/*b*/
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType<U> = U;
type A<T, U> = () => <T>(v: T) => (v: T) => <T>(v: T) => NewType<U>`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type A<T, U> = () => <T>(v: T) => (v: T) => /*a*/<T>(v: T) => U/*b*/
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType<U> = <T>(v: T) => U;
type A<T, U> = () => <T>(v: T) => (v: T) => NewType<U>`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type A<T, U> = () => <T>(v: T) => /*a*/(v: T) => <T>(v: T) => U/*b*/
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType<T, U> = (v: T) => <T>(v: T) => U;
type A<T, U> = () => <T>(v: T) => NewType<T, U>`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type A<T, U> = () => /*a*/<T>(v: T) => (v: T) => <T>(v: T) => U/*b*/
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType<U> = <T>(v: T) => (v: T) => <T>(v: T) => U;
type A<T, U> = () => NewType<U>`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type A<T, U> = /*a*/() => <T>(v: T) => (v: T) => <T>(v: T) => U/*b*/
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType<U> = () => <T>(v: T) => (v: T) => <T>(v: T) => U;
type A<T, U> = NewType<U>`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type Item<T> = /*a*/T/*b*/ extends (infer P)[] ? P : never
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType<T> = T;
type Item<T> = NewType<T> extends (infer P)[] ? P : never`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type Item<T> = T extends (infer P)[] ? /*a*/P/*b*/ : never
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType<P> = P;
type Item<T> = T extends (infer P)[] ? NewType<P> : never`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// var x: /*a*/string | number | boolean/*b*/ = '';
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = string | number | boolean;
var x: NewType = '';`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type Item<T> = T extends (infer P)[] ? P : /*a*/never/*b*/
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = never;
type Item<T> = T extends (infer P)[] ? P : NewType`,
});
@@ -0,0 +1,6 @@
/// <reference path='fourslash.ts' />
//// type Item<T> = T extends /*a*/(infer P)[]/*b*/ ? P : never
goTo.select("a", "b");
verify.not.refactorAvailable('Extract type')
@@ -0,0 +1,6 @@
/// <reference path='fourslash.ts' />
//// type Item<T> = T extends (/*a*/infer P/*b*/)[] ? P : never
goTo.select("a", "b");
verify.not.refactorAvailable('Extract type')
@@ -0,0 +1,6 @@
/// <reference path='fourslash.ts' />
//// type Item<T> = T extends (infer /*a*/P/*b*/)[] ? P : never
goTo.select("a", "b");
verify.not.refactorAvailable('Extract type')
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type Item<T> = /*a*/T extends (infer P)[] ? P : never/*b*/
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType<T> = T extends (infer P)[] ? P : never;
type Item<T> = NewType<T>`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type Union<T, U> = /*a*/U | T/*b*/
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType<U, T> = U | T;
type Union<T, U> = NewType<U, T>`,
});
@@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
//// type A = (v: /*a*/string | number/*b*/) => v is string
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = string | number;
type A = (v: NewType) => v is string`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type A = (v: string | number) => v is /*a*/string/*b*/
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = string;
type A = (v: string | number) => v is NewType`,
});
@@ -0,0 +1,6 @@
/// <reference path='fourslash.ts' />
//// type A = (v: string | number) => /*a*/v is string/*b*/
goTo.select("a", "b");
verify.not.refactorAvailable("Extract type")
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type A = /*a*/(v: string | number) => v is string/*b*/
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = (v: string | number) => v is string;
type A = NewType`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// var x: /*a*/1/*b*/ = 1;
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = 1;
var x: NewType = 1;`,
});
@@ -0,0 +1,6 @@
/// <reference path='fourslash.ts' />
//// type A = (v: string | number) => /*a*/typeof v/*b*/
goTo.select("a", "b");
verify.not.refactorAvailable("Extract type")
@@ -0,0 +1,14 @@
/// <reference path='fourslash.ts' />
//// type A = /*a*/(v: string | number) => typeof v/*b*/
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = (v: string | number) => typeof v;
type A = NewType`,
});
@@ -0,0 +1,15 @@
/// <reference path='fourslash.ts' />
//// const a = 1
//// type A = (v: string | number) => /*a*/typeof a/*b*/
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `const a = 1
type /*RENAME*/NewType = typeof a;
type A = (v: string | number) => NewType`,
});
@@ -0,0 +1,6 @@
/// <reference path='fourslash.ts' />
//// type A = (v: string | number) => /*a*/number | typeof v/*b*/
goTo.select("a", "b");
verify.not.refactorAvailable("Extract type")
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type A<T> = /*a*/B.C.D<T>/*b*/
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType<T> = B.C.D<T>;
type A<T> = NewType<T>`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type A = /*a*/B.C.D/*b*/
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = B.C.D;
type A = NewType`,
});
@@ -0,0 +1,15 @@
/// <reference path='fourslash.ts' />
//// namespace A { export const b = 1 }
//// function a(b: string): /*a*/typeof A.b/*b*/ { return 1 }
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `namespace A { export const b = 1 }
type /*RENAME*/NewType = typeof A.b;
function a(b: string): NewType { return 1 }`,
});
@@ -0,0 +1,6 @@
/// <reference path='fourslash.ts' />
//// type Crazy<T> = T extends [infer P, (/*a*/infer R extends string ? string : never/*b*/)] ? P & R : string;
goTo.select("a", "b");
verify.not.refactorAvailable("Extract type")
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// type Crazy<T> = /*a*/T extends [infer P, (infer R extends string ? string : never)] ? P & R : string/*b*/;
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType<T> = T extends [infer P, (infer R extends string ? string : never)] ? P & R : string;
type Crazy<T> = NewType<T>;`,
});
@@ -0,0 +1,15 @@
/// <reference path='fourslash.ts' />
//// type A<T extends string> = T
//// type B<T extends string> = /*a*/A<T>/*b*/
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type A<T extends string> = T
type /*RENAME*/NewType<T extends string> = A<T>;
type B<T extends string> = NewType<T>`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// var x: /*a*/1 | 2/*b*/ = 1;
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = 1 | 2;
var x: NewType = 1;`,
});
@@ -0,0 +1,6 @@
/// <reference path='fourslash.ts' />
//// interface I { f: (this: O, b: number) => /*a*/typeof this.a/*b*/ };
goTo.select("a", "b");
verify.not.refactorAvailable("Extract type")
@@ -0,0 +1,6 @@
/// <reference path='fourslash.ts' />
//// interface I { f: (this: O, b: number) => /*a*/this/*b*/ };
goTo.select("a", "b");
verify.not.refactorAvailable("Extract type")
@@ -0,0 +1,6 @@
/// <reference path='fourslash.ts' />
//// interface I { f: (this: O, b: number) => /*a*/typeof this["a"]/*b*/ };
goTo.select("a", "b");
verify.not.refactorAvailable("Extract type")
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// interface I { f: /*a*/(this: O, b: number) => typeof this.a/*b*/ };
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = (this: O, b: number) => typeof this.a;
interface I { f: NewType };`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// interface I { f: /*a*/(this: O, b: number) => this/*b*/ };
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = (this: O, b: number) => this;
interface I { f: NewType };`,
});
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// interface I { f: /*a*/(this: O, b: number) => typeof this["a"]/*b*/ };
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = (this: O, b: number) => typeof this["a"];
interface I { f: NewType };`,
});
@@ -0,0 +1,7 @@
/// <reference path='fourslash.ts' />
// typeof other parameters within function signature?
//// function f(a: string, b: /*a*/typeof a/*b*/): typeof b { return ''; }
goTo.select("a", "b");
verify.not.refactorAvailable("Extract type")
@@ -0,0 +1,27 @@
/// <reference path='fourslash.ts' />
// Where do lines get inserted?
// The exact structure here doesn't matter,
// just want to see something within a block body
// to have the behavior defined in tests.
//// function id<T>(x: T): T {
//// return (() => {
//// const s: /*a*/typeof x/*b*/ = x;
//// return s;
//// })();
//// }
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `function id<T>(x: T): T {
return (() => {
type /*RENAME*/NewType = typeof x;
const s: NewType = x;
return s;
})();
}`,
});
@@ -0,0 +1,6 @@
/// <reference path='fourslash.ts' />
//// interface I { f: (this: O, b: number) => /*a*/ true | this | false /*b*/ };
goTo.select("a", "b");
verify.not.refactorAvailable("Extract type")
@@ -0,0 +1,10 @@
/// <reference path='fourslash.ts' />
//// class C {
//// m<T>(): /*a*/T | this | number/*b*/ {
//// return {} as any
//// }
//// }
goTo.select("a", "b");
verify.not.refactorAvailable("Extract type")
@@ -0,0 +1,13 @@
/// <reference path='fourslash.ts' />
//// var x: 1 | /*a*/2/*b*/ = 1;
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = 2;
var x: 1 | NewType = 1;`,
});
@@ -0,0 +1,17 @@
/// <reference path='fourslash.ts' />
//// function foo(a: /*a*/number/*b*/, b?: number, ...c: number[]): boolean {
//// return false as boolean
//// }
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = number;
function foo(a: NewType, b?: number, ...c: number[]): boolean {
return false as boolean
}`,
});
@@ -0,0 +1,17 @@
/// <reference path='fourslash.ts' />
//// function foo(a: number, b?: /*a*/number/*b*/, ...c: number[]): boolean {
//// return false as boolean
//// }
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = number;
function foo(a: number, b?: NewType, ...c: number[]): boolean {
return false as boolean
}`,
});
@@ -0,0 +1,17 @@
/// <reference path='fourslash.ts' />
//// function foo(a: number, b?: number, ...c: /*a*/number[]/*b*/): boolean {
//// return false as boolean
//// }
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to type alias",
actionDescription: "Extract to type alias",
newContent: `type /*RENAME*/NewType = number[];
function foo(a: number, b?: number, ...c: NewType): boolean {
return false as boolean
}`,
});
@@ -0,0 +1,20 @@
/// <reference path='fourslash.ts' />
// @allowJs: true
// @Filename: a.js
//// /** @type { /*a*/string/*b*/ } */
//// var x;
goTo.file('a.js')
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to typedef",
actionDescription: "Extract to typedef",
newContent: `/**
* @typedef {string} /*RENAME*/NewType
*/
/** @type { NewType } */
var x;`,
});
@@ -0,0 +1,20 @@
/// <reference path='fourslash.ts' />
// @allowJs: true
// @Filename: a.js
//// /** @type { /*a*/string/*b*/ | number } */
//// var x;
goTo.file('a.js')
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to typedef",
actionDescription: "Extract to typedef",
newContent: `/**
* @typedef {string} /*RENAME*/NewType
*/
/** @type { NewType | number } */
var x;`,
});
@@ -0,0 +1,20 @@
/// <reference path='fourslash.ts' />
// @allowJs: true
// @Filename: a.js
//// /** @type { /*a*/string | number/*b*/ } */
//// var x;
goTo.file('a.js')
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to typedef",
actionDescription: "Extract to typedef",
newContent: `/**
* @typedef {string | number} /*RENAME*/NewType
*/
/** @type { NewType } */
var x;`,
});
@@ -0,0 +1,34 @@
/// <reference path='fourslash.ts' />
// @allowJs: true
// @Filename: a.js
//// /**
//// * @template T
//// * @template U
//// * @param {T} b
//// * @param {U} c
//// * @returns {/*a*/T | U/*b*/}
//// */
//// function a(b, c) {}
goTo.file('a.js')
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to typedef",
actionDescription: "Extract to typedef",
newContent: `/**
* @template T
* @template U
* @typedef {T | U} /*RENAME*/NewType
*/
/**
* @template T
* @template U
* @param {T} b
* @param {U} c
* @returns {NewType<T, U>}
*/
function a(b, c) {}`,
});
@@ -0,0 +1,34 @@
/// <reference path='fourslash.ts' />
// @allowJs: true
// @Filename: a.js
//// /**
//// * @template {number} T
//// * @template {string} U
//// * @param {T} b
//// * @param {U} c
//// * @returns {/*a*/T | U/*b*/}
//// */
//// function a(b, c) {}
goTo.file('a.js')
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to typedef",
actionDescription: "Extract to typedef",
newContent: `/**
* @template {number} T
* @template {string} U
* @typedef {T | U} /*RENAME*/NewType
*/
/**
* @template {number} T
* @template {string} U
* @param {T} b
* @param {U} c
* @returns {NewType<T, U>}
*/
function a(b, c) {}`,
});
@@ -0,0 +1,32 @@
/// <reference path='fourslash.ts' />
// @allowJs: true
// @Filename: a.js
//// /**
//// * @template {number} T, U
//// * @param {T} b
//// * @param {U} c
//// * @returns {/*a*/T | U/*b*/}
//// */
//// function a(b, c) {}
goTo.file('a.js')
goTo.select("a", "b");
edit.applyRefactor({
refactorName: "Extract type",
actionName: "Extract to typedef",
actionDescription: "Extract to typedef",
newContent: `/**
* @template {number} T
* @template U
* @typedef {T | U} /*RENAME*/NewType
*/
/**
* @template {number} T, U
* @param {T} b
* @param {U} c
* @returns {NewType<T, U>}
*/
function a(b, c) {}`,
});