mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
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:
committed by
Nathan Shively-Sanders
parent
6c4876a00a
commit
714821fc97
@@ -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
|
||||
|
||||
@@ -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))));
|
||||
}
|
||||
}
|
||||
@@ -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) {}`,
|
||||
});
|
||||
Reference in New Issue
Block a user