diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index af3ecad79a669..87101781eadf7 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -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 diff --git a/src/services/refactors/extractType.ts b/src/services/refactors/extractType.ts new file mode 100644 index 0000000000000..02f6cd39a1885 --- /dev/null +++ b/src/services/refactors/extractType.ts @@ -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 { + 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; } + + 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) { + 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) { + const node = 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 = createNode(SyntaxKind.JSDocTemplateTag); + template.tagName = createIdentifier("template"); + template.constraint = constraint && cast(constraint, isJSDocTypeExpression); + + const parameter = createNode(SyntaxKind.TypeParameter); + parameter.name = typeParameter.name; + template.typeParameters = createNodeArray([parameter]); + + templates.push(template); + }); + + changes.insertNodeBefore(file, firstStatement, createJSDocComment(/* comment */ undefined, createNodeArray(concatenate(templates, [node]))), /* blankLineBetween */ true); + changes.replaceNode(file, selection, createTypeReferenceNode(name, typeParameters.map(id => createTypeReferenceNode(id.name, /* typeArguments */ undefined)))); + } +} diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 93880ea0d9b10..6b78d5c7604a8 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -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", diff --git a/tests/cases/fourslash/refactorExtractType1.ts b/tests/cases/fourslash/refactorExtractType1.ts new file mode 100644 index 0000000000000..72a76280ca0b1 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType1.ts @@ -0,0 +1,16 @@ +/// + +//// 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 = { };`, +}); diff --git a/tests/cases/fourslash/refactorExtractType10.ts b/tests/cases/fourslash/refactorExtractType10.ts new file mode 100644 index 0000000000000..870ba7e6207f3 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType10.ts @@ -0,0 +1,17 @@ +/// + +//// 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 +}`, +}); diff --git a/tests/cases/fourslash/refactorExtractType11.ts b/tests/cases/fourslash/refactorExtractType11.ts new file mode 100644 index 0000000000000..feb5e68c44093 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType11.ts @@ -0,0 +1,17 @@ +/// + +//// 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 +}`, +}); diff --git a/tests/cases/fourslash/refactorExtractType12.ts b/tests/cases/fourslash/refactorExtractType12.ts new file mode 100644 index 0000000000000..8c4a9e316e4aa --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType12.ts @@ -0,0 +1,21 @@ +/// + +//// interface A { +//// 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 { + a: boolean + b: number + c: T +}`, +}); diff --git a/tests/cases/fourslash/refactorExtractType13.ts b/tests/cases/fourslash/refactorExtractType13.ts new file mode 100644 index 0000000000000..e72eadb2dd136 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType13.ts @@ -0,0 +1,21 @@ +/// + +//// interface A { +//// 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 { + a: NewType + b: number + c: T +}`, +}); diff --git a/tests/cases/fourslash/refactorExtractType14.ts b/tests/cases/fourslash/refactorExtractType14.ts new file mode 100644 index 0000000000000..4d5667bb54001 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType14.ts @@ -0,0 +1,13 @@ +/// + +//// type A = 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 = string | number | T`, +}); diff --git a/tests/cases/fourslash/refactorExtractType15.ts b/tests/cases/fourslash/refactorExtractType15.ts new file mode 100644 index 0000000000000..a716f42509ad8 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType15.ts @@ -0,0 +1,13 @@ +/// + +//// type A = /*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 = NewType | number | T`, +}); diff --git a/tests/cases/fourslash/refactorExtractType16.ts b/tests/cases/fourslash/refactorExtractType16.ts new file mode 100644 index 0000000000000..0ccc3000b56a8 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType16.ts @@ -0,0 +1,13 @@ +/// + +//// 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 } = { };`, +}); diff --git a/tests/cases/fourslash/refactorExtractType17.ts b/tests/cases/fourslash/refactorExtractType17.ts new file mode 100644 index 0000000000000..ae62b402fe3c6 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType17.ts @@ -0,0 +1,13 @@ +/// + +//// type A = 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; + +type A = string | number | NewType`, +}); diff --git a/tests/cases/fourslash/refactorExtractType18.ts b/tests/cases/fourslash/refactorExtractType18.ts new file mode 100644 index 0000000000000..1ebe8a2b35480 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType18.ts @@ -0,0 +1,14 @@ +/// + +//// type A = /*a*/Partial/*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 = Partial; + +type A = NewType & D | C`, +}); + diff --git a/tests/cases/fourslash/refactorExtractType19.ts b/tests/cases/fourslash/refactorExtractType19.ts new file mode 100644 index 0000000000000..4a982f6899db4 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType19.ts @@ -0,0 +1,14 @@ +/// + +//// type A = /*a*/Partial/*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 = Partial; + +type A = NewType & D | C`, +}); + diff --git a/tests/cases/fourslash/refactorExtractType2.ts b/tests/cases/fourslash/refactorExtractType2.ts new file mode 100644 index 0000000000000..202de0f115ac0 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType2.ts @@ -0,0 +1,13 @@ +/// + +//// 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 = '';`, +}); diff --git a/tests/cases/fourslash/refactorExtractType20.ts b/tests/cases/fourslash/refactorExtractType20.ts new file mode 100644 index 0000000000000..b31f208704f08 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType20.ts @@ -0,0 +1,13 @@ +/// + +//// type A = () => (v: /*a*/T/*b*/) => (v: 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; + +type A = () => (v: NewType) => (v: T) => (v: T) => U`, +}); diff --git a/tests/cases/fourslash/refactorExtractType21.ts b/tests/cases/fourslash/refactorExtractType21.ts new file mode 100644 index 0000000000000..e97485c82696e --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType21.ts @@ -0,0 +1,13 @@ +/// + +//// type A = () => (v: T) => (v: /*a*/T/*b*/) => (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; + +type A = () => (v: T) => (v: NewType) => (v: T) => U`, +}); diff --git a/tests/cases/fourslash/refactorExtractType22.ts b/tests/cases/fourslash/refactorExtractType22.ts new file mode 100644 index 0000000000000..f7282df4ef707 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType22.ts @@ -0,0 +1,13 @@ +/// + +//// type A = () => (v: T) => (v: 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; + +type A = () => (v: T) => (v: T) => (v: NewType) => U`, +}); diff --git a/tests/cases/fourslash/refactorExtractType23.ts b/tests/cases/fourslash/refactorExtractType23.ts new file mode 100644 index 0000000000000..9e219dd3383dd --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType23.ts @@ -0,0 +1,13 @@ +/// + +//// type A = () => (v: T) => (v: 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; + +type A = () => (v: T) => (v: T) => (v: T) => NewType`, +}); diff --git a/tests/cases/fourslash/refactorExtractType24.ts b/tests/cases/fourslash/refactorExtractType24.ts new file mode 100644 index 0000000000000..54b36acb252f7 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType24.ts @@ -0,0 +1,13 @@ +/// + +//// type A = () => (v: T) => (v: T) => /*a*/(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 = (v: T) => U; + +type A = () => (v: T) => (v: T) => NewType`, +}); diff --git a/tests/cases/fourslash/refactorExtractType25.ts b/tests/cases/fourslash/refactorExtractType25.ts new file mode 100644 index 0000000000000..b09d04912c1d9 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType25.ts @@ -0,0 +1,13 @@ +/// + +//// type A = () => (v: T) => /*a*/(v: 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 = (v: T) => (v: T) => U; + +type A = () => (v: T) => NewType`, +}); diff --git a/tests/cases/fourslash/refactorExtractType26.ts b/tests/cases/fourslash/refactorExtractType26.ts new file mode 100644 index 0000000000000..662ce7eb8dcab --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType26.ts @@ -0,0 +1,13 @@ +/// + +//// type A = () => /*a*/(v: T) => (v: 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 = (v: T) => (v: T) => (v: T) => U; + +type A = () => NewType`, +}); diff --git a/tests/cases/fourslash/refactorExtractType27.ts b/tests/cases/fourslash/refactorExtractType27.ts new file mode 100644 index 0000000000000..5163fd2d48055 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType27.ts @@ -0,0 +1,13 @@ +/// + +//// type A = /*a*/() => (v: T) => (v: 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 = () => (v: T) => (v: T) => (v: T) => U; + +type A = NewType`, +}); diff --git a/tests/cases/fourslash/refactorExtractType28.ts b/tests/cases/fourslash/refactorExtractType28.ts new file mode 100644 index 0000000000000..81d1356ca2116 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType28.ts @@ -0,0 +1,13 @@ +/// + +//// type Item = /*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; + +type Item = NewType extends (infer P)[] ? P : never`, +}); diff --git a/tests/cases/fourslash/refactorExtractType29.ts b/tests/cases/fourslash/refactorExtractType29.ts new file mode 100644 index 0000000000000..a1fb2ac405c4f --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType29.ts @@ -0,0 +1,13 @@ +/// + +//// type Item = 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; + +type Item = T extends (infer P)[] ? NewType

: never`, +}); diff --git a/tests/cases/fourslash/refactorExtractType3.ts b/tests/cases/fourslash/refactorExtractType3.ts new file mode 100644 index 0000000000000..51185a43e006c --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType3.ts @@ -0,0 +1,13 @@ +/// + +//// 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 = '';`, +}); diff --git a/tests/cases/fourslash/refactorExtractType30.ts b/tests/cases/fourslash/refactorExtractType30.ts new file mode 100644 index 0000000000000..a0684cabb5300 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType30.ts @@ -0,0 +1,13 @@ +/// + +//// type Item = 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 extends (infer P)[] ? P : NewType`, +}); diff --git a/tests/cases/fourslash/refactorExtractType31.ts b/tests/cases/fourslash/refactorExtractType31.ts new file mode 100644 index 0000000000000..84ef679de5d78 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType31.ts @@ -0,0 +1,6 @@ +/// + +//// type Item = T extends /*a*/(infer P)[]/*b*/ ? P : never + +goTo.select("a", "b"); +verify.not.refactorAvailable('Extract type') diff --git a/tests/cases/fourslash/refactorExtractType32.ts b/tests/cases/fourslash/refactorExtractType32.ts new file mode 100644 index 0000000000000..6bfe0fd26344c --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType32.ts @@ -0,0 +1,6 @@ +/// + +//// type Item = T extends (/*a*/infer P/*b*/)[] ? P : never + +goTo.select("a", "b"); +verify.not.refactorAvailable('Extract type') diff --git a/tests/cases/fourslash/refactorExtractType33.ts b/tests/cases/fourslash/refactorExtractType33.ts new file mode 100644 index 0000000000000..0124698325f8e --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType33.ts @@ -0,0 +1,6 @@ +/// + +//// type Item = T extends (infer /*a*/P/*b*/)[] ? P : never + +goTo.select("a", "b"); +verify.not.refactorAvailable('Extract type') diff --git a/tests/cases/fourslash/refactorExtractType34.ts b/tests/cases/fourslash/refactorExtractType34.ts new file mode 100644 index 0000000000000..114d08a09a6cf --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType34.ts @@ -0,0 +1,13 @@ +/// + +//// type Item = /*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 extends (infer P)[] ? P : never; + +type Item = NewType`, +}); diff --git a/tests/cases/fourslash/refactorExtractType35.ts b/tests/cases/fourslash/refactorExtractType35.ts new file mode 100644 index 0000000000000..8ace9f929b8b9 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType35.ts @@ -0,0 +1,13 @@ +/// + +//// type Union = /*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; + +type Union = NewType`, +}); diff --git a/tests/cases/fourslash/refactorExtractType36.ts b/tests/cases/fourslash/refactorExtractType36.ts new file mode 100644 index 0000000000000..5086bf130cecc --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType36.ts @@ -0,0 +1,14 @@ +/// + +//// 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`, +}); + diff --git a/tests/cases/fourslash/refactorExtractType37.ts b/tests/cases/fourslash/refactorExtractType37.ts new file mode 100644 index 0000000000000..b24e219b3e4e3 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType37.ts @@ -0,0 +1,13 @@ +/// + +//// 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`, +}); diff --git a/tests/cases/fourslash/refactorExtractType38.ts b/tests/cases/fourslash/refactorExtractType38.ts new file mode 100644 index 0000000000000..b74091f1aec42 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType38.ts @@ -0,0 +1,6 @@ +/// + +//// type A = (v: string | number) => /*a*/v is string/*b*/ + +goTo.select("a", "b"); +verify.not.refactorAvailable("Extract type") diff --git a/tests/cases/fourslash/refactorExtractType39.ts b/tests/cases/fourslash/refactorExtractType39.ts new file mode 100644 index 0000000000000..1f55dcee39aaf --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType39.ts @@ -0,0 +1,13 @@ +/// + +//// 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`, +}); diff --git a/tests/cases/fourslash/refactorExtractType4.ts b/tests/cases/fourslash/refactorExtractType4.ts new file mode 100644 index 0000000000000..def60d0b643f5 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType4.ts @@ -0,0 +1,13 @@ +/// + +//// 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;`, +}); diff --git a/tests/cases/fourslash/refactorExtractType40.ts b/tests/cases/fourslash/refactorExtractType40.ts new file mode 100644 index 0000000000000..cae32cec78618 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType40.ts @@ -0,0 +1,6 @@ +/// + +//// type A = (v: string | number) => /*a*/typeof v/*b*/ + +goTo.select("a", "b"); +verify.not.refactorAvailable("Extract type") diff --git a/tests/cases/fourslash/refactorExtractType41.ts b/tests/cases/fourslash/refactorExtractType41.ts new file mode 100644 index 0000000000000..b0d3222a456e3 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType41.ts @@ -0,0 +1,14 @@ +/// + +//// 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`, +}); + diff --git a/tests/cases/fourslash/refactorExtractType42.ts b/tests/cases/fourslash/refactorExtractType42.ts new file mode 100644 index 0000000000000..29692ab06e6a3 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType42.ts @@ -0,0 +1,15 @@ +/// + +//// 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`, +}); diff --git a/tests/cases/fourslash/refactorExtractType43.ts b/tests/cases/fourslash/refactorExtractType43.ts new file mode 100644 index 0000000000000..c3c0900929516 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType43.ts @@ -0,0 +1,6 @@ +/// + +//// type A = (v: string | number) => /*a*/number | typeof v/*b*/ + +goTo.select("a", "b"); +verify.not.refactorAvailable("Extract type") diff --git a/tests/cases/fourslash/refactorExtractType44.ts b/tests/cases/fourslash/refactorExtractType44.ts new file mode 100644 index 0000000000000..22e1a9172dc82 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType44.ts @@ -0,0 +1,13 @@ +/// + +//// 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`, +}); diff --git a/tests/cases/fourslash/refactorExtractType45.ts b/tests/cases/fourslash/refactorExtractType45.ts new file mode 100644 index 0000000000000..60d9d77bc29eb --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType45.ts @@ -0,0 +1,13 @@ +/// + +//// 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`, +}); diff --git a/tests/cases/fourslash/refactorExtractType46.ts b/tests/cases/fourslash/refactorExtractType46.ts new file mode 100644 index 0000000000000..ddcfa4878c9c1 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType46.ts @@ -0,0 +1,15 @@ +/// + +//// 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 }`, +}); diff --git a/tests/cases/fourslash/refactorExtractType47.ts b/tests/cases/fourslash/refactorExtractType47.ts new file mode 100644 index 0000000000000..e2062029fa327 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType47.ts @@ -0,0 +1,6 @@ +/// + +//// type Crazy = T extends [infer P, (/*a*/infer R extends string ? string : never/*b*/)] ? P & R : string; + +goTo.select("a", "b"); +verify.not.refactorAvailable("Extract type") \ No newline at end of file diff --git a/tests/cases/fourslash/refactorExtractType48.ts b/tests/cases/fourslash/refactorExtractType48.ts new file mode 100644 index 0000000000000..d10af6bb6379e --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType48.ts @@ -0,0 +1,13 @@ +/// + +//// type Crazy = /*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 extends [infer P, (infer R extends string ? string : never)] ? P & R : string; + +type Crazy = NewType;`, +}); diff --git a/tests/cases/fourslash/refactorExtractType49.ts b/tests/cases/fourslash/refactorExtractType49.ts new file mode 100644 index 0000000000000..7d965ee3d09e8 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType49.ts @@ -0,0 +1,15 @@ +/// + +//// type A = T +//// type B = /*a*/A/*b*/ + +goTo.select("a", "b"); +edit.applyRefactor({ + refactorName: "Extract type", + actionName: "Extract to type alias", + actionDescription: "Extract to type alias", + newContent: `type A = T +type /*RENAME*/NewType = A; + +type B = NewType`, +}); diff --git a/tests/cases/fourslash/refactorExtractType5.ts b/tests/cases/fourslash/refactorExtractType5.ts new file mode 100644 index 0000000000000..72f6cd448e47d --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType5.ts @@ -0,0 +1,13 @@ +/// + +//// 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;`, +}); diff --git a/tests/cases/fourslash/refactorExtractType50.ts b/tests/cases/fourslash/refactorExtractType50.ts new file mode 100644 index 0000000000000..8405c15c912ed --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType50.ts @@ -0,0 +1,6 @@ +/// + +//// interface I { f: (this: O, b: number) => /*a*/typeof this.a/*b*/ }; + +goTo.select("a", "b"); +verify.not.refactorAvailable("Extract type") diff --git a/tests/cases/fourslash/refactorExtractType51.ts b/tests/cases/fourslash/refactorExtractType51.ts new file mode 100644 index 0000000000000..929898ad58e9f --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType51.ts @@ -0,0 +1,6 @@ +/// + +//// interface I { f: (this: O, b: number) => /*a*/this/*b*/ }; + +goTo.select("a", "b"); +verify.not.refactorAvailable("Extract type") diff --git a/tests/cases/fourslash/refactorExtractType52.ts b/tests/cases/fourslash/refactorExtractType52.ts new file mode 100644 index 0000000000000..1ded541f88bfa --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType52.ts @@ -0,0 +1,6 @@ +/// + +//// interface I { f: (this: O, b: number) => /*a*/typeof this["a"]/*b*/ }; + +goTo.select("a", "b"); +verify.not.refactorAvailable("Extract type") diff --git a/tests/cases/fourslash/refactorExtractType53.ts b/tests/cases/fourslash/refactorExtractType53.ts new file mode 100644 index 0000000000000..070d4b8c104d9 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType53.ts @@ -0,0 +1,13 @@ +/// + +//// 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 };`, +}); diff --git a/tests/cases/fourslash/refactorExtractType54.ts b/tests/cases/fourslash/refactorExtractType54.ts new file mode 100644 index 0000000000000..4b35634119849 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType54.ts @@ -0,0 +1,13 @@ +/// + +//// 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 };`, +}); diff --git a/tests/cases/fourslash/refactorExtractType55.ts b/tests/cases/fourslash/refactorExtractType55.ts new file mode 100644 index 0000000000000..61ddef7f44317 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType55.ts @@ -0,0 +1,13 @@ +/// + +//// 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 };`, +}); diff --git a/tests/cases/fourslash/refactorExtractType56.ts b/tests/cases/fourslash/refactorExtractType56.ts new file mode 100644 index 0000000000000..6362a1a995a69 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType56.ts @@ -0,0 +1,7 @@ +/// + +// 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") diff --git a/tests/cases/fourslash/refactorExtractType57.ts b/tests/cases/fourslash/refactorExtractType57.ts new file mode 100644 index 0000000000000..7033e5e208613 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType57.ts @@ -0,0 +1,27 @@ +/// + +// 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(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(x: T): T { + return (() => { + type /*RENAME*/NewType = typeof x; + + const s: NewType = x; + return s; + })(); +}`, +}); diff --git a/tests/cases/fourslash/refactorExtractType58.ts b/tests/cases/fourslash/refactorExtractType58.ts new file mode 100644 index 0000000000000..54e58153f2b5f --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType58.ts @@ -0,0 +1,6 @@ +/// + +//// interface I { f: (this: O, b: number) => /*a*/ true | this | false /*b*/ }; + +goTo.select("a", "b"); +verify.not.refactorAvailable("Extract type") \ No newline at end of file diff --git a/tests/cases/fourslash/refactorExtractType59.ts b/tests/cases/fourslash/refactorExtractType59.ts new file mode 100644 index 0000000000000..d5fced9611bf3 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType59.ts @@ -0,0 +1,10 @@ +/// + +//// class C { +//// m(): /*a*/T | this | number/*b*/ { +//// return {} as any +//// } +//// } + +goTo.select("a", "b"); +verify.not.refactorAvailable("Extract type") diff --git a/tests/cases/fourslash/refactorExtractType6.ts b/tests/cases/fourslash/refactorExtractType6.ts new file mode 100644 index 0000000000000..437bc182af38d --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType6.ts @@ -0,0 +1,13 @@ +/// + +//// 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;`, +}); diff --git a/tests/cases/fourslash/refactorExtractType7.ts b/tests/cases/fourslash/refactorExtractType7.ts new file mode 100644 index 0000000000000..a5b1f4c7dd049 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType7.ts @@ -0,0 +1,17 @@ +/// + +//// 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 +}`, +}); diff --git a/tests/cases/fourslash/refactorExtractType8.ts b/tests/cases/fourslash/refactorExtractType8.ts new file mode 100644 index 0000000000000..61121c6737457 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType8.ts @@ -0,0 +1,17 @@ +/// + +//// 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 +}`, +}); diff --git a/tests/cases/fourslash/refactorExtractType9.ts b/tests/cases/fourslash/refactorExtractType9.ts new file mode 100644 index 0000000000000..921f2a005a507 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType9.ts @@ -0,0 +1,17 @@ +/// + +//// 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 +}`, +}); diff --git a/tests/cases/fourslash/refactorExtractType_js1.ts b/tests/cases/fourslash/refactorExtractType_js1.ts new file mode 100644 index 0000000000000..e90c74d0116bd --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType_js1.ts @@ -0,0 +1,20 @@ +/// + +// @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;`, +}); diff --git a/tests/cases/fourslash/refactorExtractType_js2.ts b/tests/cases/fourslash/refactorExtractType_js2.ts new file mode 100644 index 0000000000000..592f0f6f16323 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType_js2.ts @@ -0,0 +1,20 @@ +/// + +// @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;`, +}); diff --git a/tests/cases/fourslash/refactorExtractType_js3.ts b/tests/cases/fourslash/refactorExtractType_js3.ts new file mode 100644 index 0000000000000..9d431b2fd0868 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType_js3.ts @@ -0,0 +1,20 @@ +/// + +// @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;`, +}); diff --git a/tests/cases/fourslash/refactorExtractType_js4.ts b/tests/cases/fourslash/refactorExtractType_js4.ts new file mode 100644 index 0000000000000..1d4d4c3cdf7fd --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType_js4.ts @@ -0,0 +1,34 @@ +/// + +// @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} + */ +function a(b, c) {}`, +}); diff --git a/tests/cases/fourslash/refactorExtractType_js5.ts b/tests/cases/fourslash/refactorExtractType_js5.ts new file mode 100644 index 0000000000000..20df67d9c7682 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType_js5.ts @@ -0,0 +1,34 @@ +/// + +// @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} + */ +function a(b, c) {}`, +}); diff --git a/tests/cases/fourslash/refactorExtractType_js6.ts b/tests/cases/fourslash/refactorExtractType_js6.ts new file mode 100644 index 0000000000000..581e2cca1db24 --- /dev/null +++ b/tests/cases/fourslash/refactorExtractType_js6.ts @@ -0,0 +1,32 @@ +/// + +// @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} + */ +function a(b, c) {}`, +});