Skip to content

Commit

Permalink
feat: add Node.hasStructure type guard.
Browse files Browse the repository at this point in the history
  • Loading branch information
dsherret committed Nov 6, 2021
1 parent 0e9e916 commit 0f7d9be
Show file tree
Hide file tree
Showing 3 changed files with 29 additions and 3 deletions.
11 changes: 10 additions & 1 deletion packages/ts-morph/src/compiler/ast/common/Node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { getNextMatchingPos, getNextNonWhitespacePos, getPreviousNonWhitespacePo
replaceSourceFileTextForFormatting, replaceSourceFileTextStraight, hasNewLineInRange } from "../../../manipulation";
import { WriterFunction } from "../../../types";
import { getParentSyntaxList, getTextFromStringOrWriter, isStringKind, printNode, PrintNodeOptions, CharCodes } from "../../../utils";
import { Structure } from "../../../structures";
import { Structure, Structures } from "../../../structures";
import { FormatCodeSettings } from "../../tools";
import { Symbol } from "../../symbols";
import { Type } from "../../types";
Expand Down Expand Up @@ -2120,6 +2120,15 @@ export class Node<NodeType extends ts.Node = ts.Node> {
return (node as any).getBody?.() != null;
}

/**
* Gets if the node has a structure.
* @param node - Node to check.
*/
static hasStructure<T extends compiler.Node>(node: T): node is T & { getStructure(): Structures; } {
// this method is manually maintained
return typeof (node as any).getStructure === "function";
}

/** Creates a type guard for syntax kinds. */
static is<TKind extends keyof KindToNodeMappings>(kind: TKind): (node: compiler.Node | undefined) => node is KindToNodeMappings[TKind] {
return (node: compiler.Node | undefined): node is KindToNodeMappings[TKind] => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -784,7 +784,7 @@ export function StatementedNode<T extends Constructor<StatementedNodeExtensionTy
getModuleOrThrow(nameOrFindFunction: string | ((declaration: ModuleDeclaration) => boolean)): ModuleDeclaration {
return errors.throwIfNullOrUndefined(
this.getModule(nameOrFindFunction),
() => getNotFoundErrorMessageForNameOrFindFunction("namespace", nameOrFindFunction),
() => getNotFoundErrorMessageForNameOrFindFunction("module", nameOrFindFunction),
);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { expect } from "chai";
import { assert, IsExact } from "conditional-type-checks";
import { ClassDeclaration, Node } from "../../../../compiler";
import { ClassDeclaration, ExpressionStatement, Node } from "../../../../compiler";
import { Structures } from "../../../../structures";
import { getInfoFromText } from "../../testHelpers";

describe(nameof(Node), () => {
Expand Down Expand Up @@ -48,6 +49,22 @@ describe(nameof(Node), () => {
});
});

describe(nameof(Node.hasStructure), () => {
it("should have a structure when it does", () => {
const { firstChild } = getInfoFromText<Node>("class MyClass {}");
expect(Node.hasName(firstChild)).to.be.true;
if (Node.hasStructure(firstChild)) {
assert<IsExact<typeof firstChild, Node & { getStructure(): Structures; }>>(true);
expect((firstChild.getStructure() as any).name).to.equal("MyClass");
}
});

it("should not have a structure when it doesn't", () => {
const { firstChild } = getInfoFromText<ExpressionStatement>("func();");
expect(Node.hasStructure(firstChild.getExpression().getChildren()[0])).to.be.false;
});
});

describe(nameof(Node.isNode), () => {
const { firstChild, project } = getInfoFromText("class MyClass {}");

Expand Down

0 comments on commit 0f7d9be

Please sign in to comment.