Skip to content

Commit

Permalink
fix: #692 - Fix getParent() for TS 3.6.
Browse files Browse the repository at this point in the history
  • Loading branch information
dsherret committed Aug 31, 2019
1 parent 67f25a8 commit 37476f8
Show file tree
Hide file tree
Showing 11 changed files with 675 additions and 53 deletions.
614 changes: 609 additions & 5 deletions lib/ts-morph.d.ts

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion package.json
Expand Up @@ -17,7 +17,7 @@
"test:performance": "yarn build && node ./dist/tests/performance/run.js",
"test:performance-save": "yarn test:performance --save",
"type-check-library": "ts-node --transpile-only scripts/typeCheckLibrary",
"type-check-scripts": "tsc --noEmit --project tsconfig.scripts.json",
"type-check-scripts": "tsc --noEmit --project scripts/tsconfig.json",
"code-generate": "ts-node --transpile-only --compiler ttypescript scripts/generation/main",
"refactor": "ts-node --transpile-only scripts/refactor",
"output-wrapped-nodes": "ts-node --transpile-only scripts/generation/outputWrappedNodesInfo",
Expand Down
4 changes: 2 additions & 2 deletions scripts/common/cloning.ts
@@ -1,4 +1,4 @@
import { StatementedNode, NamespaceDeclaration } from "ts-morph";
import { StatementedNode, NamespaceDeclaration, FunctionDeclarationStructure } from "ts-morph";

export function cloneNamespaces(node: StatementedNode, cloningNamespaces: NamespaceDeclaration[]) {
const namespaces = node.addNamespaces(cloningNamespaces.map(n => ({
Expand All @@ -17,7 +17,7 @@ export function cloneNamespaces(node: StatementedNode, cloningNamespaces: Namesp
isExported: true
})));
namespaces[i].addFunctions(cloningNamespaces[i].getFunctions().map(f => ({
...f.getStructure(),
...f.getStructure() as FunctionDeclarationStructure,
isExported: true
})));
namespaces[i].addEnums(cloningNamespaces[i].getEnums().map(e => ({
Expand Down
39 changes: 38 additions & 1 deletion scripts/generation/createDeclarationFile.ts
Expand Up @@ -7,7 +7,8 @@
*/
import * as os from "os";
import { Node, TypeGuards, Scope, ClassDeclaration, StructureKind, InterfaceDeclarationStructure, TypeAliasDeclarationStructure, FunctionDeclarationStructure,
VariableStatementStructure } from "ts-morph";
VariableStatementStructure,
Type} from "ts-morph";
import { createDeclarationProject, forEachTypeText } from "../common";
import { getDeclarationFileStatements } from "./declarationFile";

Expand Down Expand Up @@ -64,6 +65,8 @@ export async function createDeclarationFile() {
makeConstructorsPrivate();
log("Removing @skipOrThrowCheck...");
removeSkipOrThrowCheck();
log("Adding getParent methods...");
addGetParentMethods();
log("Moving file...");
mainFile.move("ts-morph.d.ts");
finishLog(lastDateTime!);
Expand Down Expand Up @@ -134,6 +137,40 @@ export async function createDeclarationFile() {
}
}

function addGetParentMethods() {
for (const classDec of mainFile.getClasses()) {
const type = classDec.getType();
if (type.getProperty("compilerNode") == null)
continue;
const nodeType = type.getBaseTypes()[0];
if (nodeType == null)
continue;
const typeArgName = getTypeScriptTypeName(nodeType, classDec);
if (typeArgName == null)
continue;

classDec.addMembers(writer => {
writer.writeLine("/** @inheritdoc **/");
writer.writeLine(`getParent(): NodeParentType<${typeArgName}>;`);
writer.writeLine("/** @inheritdoc **/");
writer.writeLine(`getParentOrThrow(): NonNullable<NodeParentType<${typeArgName}>>;`);
});
}

function getTypeScriptTypeName(nodeType: Type, classDec: ClassDeclaration) {
const types = [nodeType, ...nodeType.getIntersectionTypes()];
for (const type of types) {
for (const typeArg of type.getTypeArguments()) {
const typeArgName = typeArg.getText(classDec);
if (typeArgName.startsWith("ts."))
return typeArgName;
}
}

return undefined;
}
}

function log(message: string) {
if (lastDateTime != null)
finishLog(lastDateTime);
Expand Down
2 changes: 1 addition & 1 deletion scripts/generation/createForEachStructureChild.ts
Expand Up @@ -176,7 +176,7 @@ function getStructureInfos(inspector: TsMorphInspector) {
const kinds: string[] = [];
let allStructure = true;
for (const arrayType of arrayTypes) {
const arrayElementType = arrayType.getArrayElementType();
const arrayElementType = arrayType.getArrayElementTypeOrThrow();
for (const unionElementType of getTypeOrUnionElementTypes(arrayElementType)) {
const kindProperty = getTypeKindProperty(unionElementType);
if (kindProperty != null)
Expand Down
10 changes: 10 additions & 0 deletions scripts/tsconfig.json
@@ -0,0 +1,10 @@
{
"extends": "../tsconfig.common.json",
"compilerOptions": {
"noEmit": true,
"declaration": false,
"rootDirs": ["../scripts", "../src"],
"outDir": "../dist-scripts",
"plugins": [{ "transform": "ts-nameof", "type": "raw" }]
}
}
2 changes: 1 addition & 1 deletion src/compiler/ast/class/ClassElement.ts
Expand Up @@ -9,7 +9,7 @@ export class ClassElement<T extends ts.ClassElement = ts.ClassElement> extends N
* Removes the class member.
*/
remove() {
const parent = this.getParent();
const parent = this.getParentOrThrow();
if (TypeGuards.isClassDeclaration(parent) || TypeGuards.isClassExpression(parent))
removeClassMember(this);
else if (TypeGuards.isObjectLiteralExpression(parent))
Expand Down
10 changes: 5 additions & 5 deletions src/compiler/ast/common/Node.ts
Expand Up @@ -29,7 +29,7 @@ export type NodePropertyToWrappedType<NodeType extends ts.Node, KeyName extends
: NonNullableNodeType extends ts.Node ? CompilerNodeToWrappedType<NonNullableNodeType> | undefined
: NodeType[KeyName];

export type NodeParentType<NodeType extends ts.Node> = NodeType extends ts.SourceFile ? CompilerNodeToWrappedType<NodeType["parent"]> | undefined
export type NodeParentType<NodeType extends ts.Node> = NodeType extends ts.SourceFile ? undefined
: ts.Node extends NodeType ? CompilerNodeToWrappedType<NodeType["parent"]> | undefined
: CompilerNodeToWrappedType<NodeType["parent"]>;

Expand Down Expand Up @@ -1094,15 +1094,15 @@ export class Node<NodeType extends ts.Node = ts.Node> {
/**
* Get the node's parent.
*/
getParent<T extends Node | undefined = NodeParentType<NodeType>>(): T {
return this._getNodeFromCompilerNodeIfExists(this.compilerNode.parent) as T;
getParent() {
return this._getNodeFromCompilerNodeIfExists(this.compilerNode.parent);
}

/**
* Gets the parent or throws an error if it doesn't exist.
*/
getParentOrThrow<T extends Node | undefined = NodeParentType<NodeType>>(): NonNullable<T> {
return errors.throwIfNullOrUndefined(this.getParent<T>(), "Expected to find a parent.") as NonNullable<T>;
getParentOrThrow() {
return errors.throwIfNullOrUndefined(this.getParent(), "Expected to find a parent.");
}

/**
Expand Down
4 changes: 2 additions & 2 deletions src/compiler/ast/type/TypeParameterDeclaration.ts
Expand Up @@ -31,7 +31,7 @@ export class TypeParameterDeclaration extends TypeParameterDeclarationBase<ts.Ty
* @param text - Text to set as the constraint.
*/
setConstraint(text: string | WriterFunction) {
text = this.getParent()._getTextWithQueuedChildIndentation(text);
text = this.getParentOrThrow()._getTextWithQueuedChildIndentation(text);
if (StringUtils.isNullOrWhitespace(text)) {
this.removeConstraint();
return this;
Expand Down Expand Up @@ -80,7 +80,7 @@ export class TypeParameterDeclaration extends TypeParameterDeclarationBase<ts.Ty
* @param text - Text to set as the default type node.
*/
setDefault(text: string | WriterFunction) {
text = this.getParent()._getTextWithQueuedChildIndentation(text);
text = this.getParentOrThrow()._getTextWithQueuedChildIndentation(text);
if (StringUtils.isNullOrWhitespace(text)) {
this.removeDefault();
return this;
Expand Down
26 changes: 6 additions & 20 deletions src/tests/compiler/ast/common/nodeTests.ts
Expand Up @@ -3,7 +3,7 @@ import { assert, IsExact, IsNullable } from "conditional-type-checks";
import { CodeBlockWriter } from "../../../../codeBlockWriter";
import { CallExpression, ClassDeclaration, EnumDeclaration, FormatCodeSettings, FunctionDeclaration, Identifier, InterfaceDeclaration, Node,
PropertyAccessExpression, PropertySignature, SourceFile, TypeParameterDeclaration, ForEachDescendantTraversalControl, VariableStatement, ForStatement,
ForOfStatement, ForInStatement, NumericLiteral, StringLiteral, ExpressionStatement } from "../../../../compiler";
ForOfStatement, ForInStatement, NumericLiteral, StringLiteral, ExpressionStatement, NodeParentType } from "../../../../compiler";
import { hasParsedTokens } from "../../../../compiler/ast/utils";
import { Project } from "../../../../Project";
import * as errors from "../../../../errors";
Expand Down Expand Up @@ -502,31 +502,17 @@ class MyClass {
});
});

describe(nameof<Node>(n => n.getParent), () => {
describe(nameof<NodeParentType<any>>(), () => {
it("should have the correct type when it will have a parent", () => {
const { firstChild } = getInfoFromText<VariableStatement>("const t = 5;");
const parent = firstChild.getDeclarationList().getParent();
assert<IsExact<typeof parent, VariableStatement | ForStatement | ForOfStatement | ForInStatement>>(true);
assert<IsExact<NodeParentType<ts.VariableDeclarationList>, VariableStatement | ForStatement | ForOfStatement | ForInStatement>>(true);
});

it("should have the correct type when it might not have a parent", () => {
const { firstChild } = getInfoFromText<VariableStatement>("const t = 5;");
const parent = (firstChild as Node).getParent();
assert<IsExact<typeof parent, Node | undefined>>(true);
assert<IsExact<NodeParentType<ts.Node>, Node | undefined>>(true);
});
});

describe(nameof<Node>(n => n.getParentOrThrow), () => {
it("should have the correct type when it will have a parent", () => {
const { firstChild } = getInfoFromText<VariableStatement>("const t = 5;");
const parent = firstChild.getDeclarationList().getParentOrThrow();
assert<IsExact<typeof parent, VariableStatement | ForStatement | ForOfStatement | ForInStatement>>(true);
});

it("should have the correct type when it might not have a parent", () => {
const { firstChild } = getInfoFromText<VariableStatement>("const t = 5;");
const parent = (firstChild as Node).getParentOrThrow();
assert<IsExact<typeof parent, Node>>(true);
it("should have the correct type when it's a source file", () => {
assert<IsExact<NodeParentType<ts.SourceFile>, undefined>>(true);
});
});

Expand Down
15 changes: 0 additions & 15 deletions tsconfig.scripts.json

This file was deleted.

0 comments on commit 37476f8

Please sign in to comment.