diff --git a/deno/ts_morph.d.ts b/deno/ts_morph.d.ts index 1df05ae12..6ff1cbcff 100644 --- a/deno/ts_morph.d.ts +++ b/deno/ts_morph.d.ts @@ -3393,6 +3393,13 @@ export declare class Node { * @param kind - Syntax kind. */ asKindOrThrow(kind: TKind): KindToNodeMappings[TKind]; + /** + * Returns if the node is the specified kind. + * + * This is a type guard. + * @param kind - Syntax kind. + */ + isKind(kind: TKind): this is KindToNodeMappings[TKind]; /** * Gets the node as the specified kind if it is equal to that kind, otherwise returns undefined. * @param kind - Syntax kind. @@ -6946,7 +6953,7 @@ export declare class TemplateExpression extends TemplateExpressionBase; /** @inheritdoc **/ getParent(): NodeParentType; /** @inheritdoc **/ @@ -8881,6 +8888,18 @@ export declare class ParenthesizedTypeNode extends TypeNode { + /** Gets the template head. */ + getHead(): TemplateHead; + /** Gets the template spans. */ + getTemplateSpans(): TypeNode[]; + /** + * Sets the literal value. + * + * Note: This could possibly replace the node if you remove all the tagged templates. + * @param value - Value to set. + * @returns The new node if the kind changed; the current node otherwise. + */ + setLiteralValue(value: string): Node; /** @inheritdoc **/ getParent(): NodeParentType; /** @inheritdoc **/ diff --git a/deno/ts_morph.js b/deno/ts_morph.js index f3f95e717..89fc9e2b8 100644 --- a/deno/ts_morph.js +++ b/deno/ts_morph.js @@ -3112,8 +3112,11 @@ class Node { asKindOrThrow(kind) { return errors.throwIfNullOrUndefined(this.asKind(kind), () => `Expected the node to be of kind ${getSyntaxKindName(kind)}, but it was ${getSyntaxKindName(this.getKind())}.`); } + isKind(kind) { + return this.getKind() === kind; + } asKind(kind) { - if (this.getKind() === kind) { + if (this.isKind(kind)) { return this; } else { @@ -15244,6 +15247,24 @@ class ParenthesizedTypeNode extends TypeNode { } class TemplateLiteralTypeNode extends TypeNode { + getHead() { + return this._getNodeFromCompilerNode(this.compilerNode.head); + } + getTemplateSpans() { + return this.compilerNode.templateSpans.map(s => this._getNodeFromCompilerNode(s)); + } + setLiteralValue(value) { + var _a; + const childIndex = this.getChildIndex(); + const parent = (_a = this.getParentSyntaxList()) !== null && _a !== void 0 ? _a : this.getParentOrThrow(); + replaceNodeText({ + sourceFile: this._sourceFile, + start: this.getStart() + 1, + replacingLength: this.getWidth() - 2, + newText: value, + }); + return parent.getChildAtIndex(childIndex); + } } class ThisTypeNode extends TypeNode { diff --git a/packages/ts-morph/lib/ts-morph.d.ts b/packages/ts-morph/lib/ts-morph.d.ts index 5def929c4..7fb95eb38 100644 --- a/packages/ts-morph/lib/ts-morph.d.ts +++ b/packages/ts-morph/lib/ts-morph.d.ts @@ -3393,6 +3393,13 @@ export declare class Node { * @param kind - Syntax kind. */ asKindOrThrow(kind: TKind): KindToNodeMappings[TKind]; + /** + * Returns if the node is the specified kind. + * + * This is a type guard. + * @param kind - Syntax kind. + */ + isKind(kind: TKind): this is KindToNodeMappings[TKind]; /** * Gets the node as the specified kind if it is equal to that kind, otherwise returns undefined. * @param kind - Syntax kind. @@ -6946,7 +6953,7 @@ export declare class TemplateExpression extends TemplateExpressionBase; /** @inheritdoc **/ getParent(): NodeParentType; /** @inheritdoc **/ @@ -8881,6 +8888,18 @@ export declare class ParenthesizedTypeNode extends TypeNode { + /** Gets the template head. */ + getHead(): TemplateHead; + /** Gets the template spans. */ + getTemplateSpans(): TypeNode[]; + /** + * Sets the literal value. + * + * Note: This could possibly replace the node if you remove all the tagged templates. + * @param value - Value to set. + * @returns The new node if the kind changed; the current node otherwise. + */ + setLiteralValue(value: string): Node; /** @inheritdoc **/ getParent(): NodeParentType; /** @inheritdoc **/ diff --git a/packages/ts-morph/src/compiler/ast/literal/template/TemplateExpression.ts b/packages/ts-morph/src/compiler/ast/literal/template/TemplateExpression.ts index e6192724c..d068b2162 100644 --- a/packages/ts-morph/src/compiler/ast/literal/template/TemplateExpression.ts +++ b/packages/ts-morph/src/compiler/ast/literal/template/TemplateExpression.ts @@ -1,6 +1,5 @@ import { ts } from "@ts-morph/common"; import { replaceNodeText } from "../../../../manipulation"; -import { TemplateLiteral } from "../../aliases"; import { PrimaryExpression } from "../../expression"; import { TemplateHead } from "./TemplateHead"; @@ -37,6 +36,6 @@ export class TemplateExpression extends TemplateExpressionBase { - // todo: helper methods + /** + * Gets the template head. + */ + getHead(): TemplateHead { + return this._getNodeFromCompilerNode(this.compilerNode.head); + } + + /** + * Gets the template spans. + */ + getTemplateSpans() { + return this.compilerNode.templateSpans.map(s => this._getNodeFromCompilerNode(s)); + } + + /** + * Sets the literal value. + * + * Note: This could possibly replace the node if you remove all the tagged templates. + * @param value - Value to set. + * @returns The new node if the kind changed; the current node otherwise. + */ + setLiteralValue(value: string) { + const childIndex = this.getChildIndex(); + const parent = this.getParentSyntaxList() ?? this.getParentOrThrow(); + replaceNodeText({ + sourceFile: this._sourceFile, + start: this.getStart() + 1, + replacingLength: this.getWidth() - 2, + newText: value, + }); + + return parent.getChildAtIndex(childIndex); + } } diff --git a/packages/ts-morph/src/tests/compiler/ast/type/templateLiteralTypeNodeTests.ts b/packages/ts-morph/src/tests/compiler/ast/type/templateLiteralTypeNodeTests.ts new file mode 100644 index 000000000..c64423b1e --- /dev/null +++ b/packages/ts-morph/src/tests/compiler/ast/type/templateLiteralTypeNodeTests.ts @@ -0,0 +1,49 @@ +import { nameof, SyntaxKind } from "@ts-morph/common"; +import { expect } from "chai"; +import { TemplateExpression, TemplateLiteralTypeNode } from "../../../../compiler"; +import { getInfoFromTextWithDescendant } from "../../testHelpers"; + +function getTypeNode(text: string) { + return getInfoFromTextWithDescendant(text, SyntaxKind.TemplateLiteralType).descendant; +} + +describe("TemplateLiteralTypeNode", () => { + describe(nameof("getHead"), () => { + function doTest(text: string, expectedText: string) { + const expression = getTypeNode(text); + expect(expression.getHead().getText()).to.equal(expectedText); + } + + it("should get the correct template head", () => { + doTest("type Test = `foo${test}`", "`foo${"); + }); + }); + + describe(nameof("getTemplateSpans"), () => { + function doTest(text: string, expectedText: string) { + const expression = getTypeNode(text); + expect(expression.getTemplateSpans()[0].getText()).to.equal(expectedText); + } + + it("should get the correct template spans", () => { + doTest("type Test = `foo${test}`", "test}`"); + }); + }); + + describe(nameof("setLiteralValue"), () => { + function doTest(text: string, newText: string, expectedText: string) { + const expression = getTypeNode(text); + const sourceFile = expression._sourceFile; + expect(expression.setLiteralValue(newText).wasForgotten()).to.be.false; + expect(sourceFile.getText()).to.equal(expectedText); + } + + it("should set the value", () => { + doTest("type Test = `foo${test}`", "testing${this}out", "type Test = `testing${this}out`"); + }); + + it("should set the value to not have any tagged templates", () => { + doTest("type Test = `foo${test}`", "testing", "type Test = `testing`"); + }); + }); +});