In ts-morph, structures represent the abstract syntax trees of source files and their descendant nodes… at least partially. If you have a structure, you can create an equivalent node from it.
ts-morph
API’sts-morph
clone
methods on every structure class and type structure class.toJSON()
methods for easy serialization (think workers or child processes)If you have an existing ts-morph structure, and you want to create a structure class instance, each structure class has a static .clone()
method. Just pass in your structure as the first argument, and it will return a new structure class object.
Often you won’t need the kind
field.
Example:
const doc = JSDocImpl.clone({
description: "Hello World";
});
doc.tags.push(new JSDocTagImpl("internal"));
Many ts-morph nodes have a getStructure()
method on them - which return raw ts-morph structure objects. This project uses getStructure()
as a starting point to bootstrap to the full structure class instances.
/**
* Get a structure for a node, with type structures installed throughout its descendants.
* @param rootNode - The node to start from.
* @param userConsole - a callback for conversion failures.
* @param assertNoFailures - if true, assert there are no conversion failures.
*
* @returns the root structure, and any failures during recursion.
*/
declare function getTypeAugmentedStructure(
rootNode: NodeWithStructures,
userConsole: TypeNodeToTypeStructureConsole,
assertNoFailures: boolean
): RootStructureWithConvertFailures;
/**
* Get a structure for a node, with type structures installed throughout its descendants.
* @param rootNode - The node to start from.
* @param userConsole - a callback for conversion failures.
* @param assertNoFailures - if true, assert there are no conversion failures.
* @param kind - the expected structure kind to retrieve.
* @returns the root structure, and any failures during recursion.
*/
declare function getTypeAugmentedStructure<TKind extends StructureKind>(
rootNode: NodeWithStructures,
userConsole: TypeNodeToTypeStructureConsole,
assertNoFailures: boolean,
kind: TKind
): RootStructureWithConvertFailures<TKind>;
interface NodeWithStructures extends Node {
getStructure(): Structures;
}
/**
* @param message - The failure message.
* @param failingTypeNode - the type node we failed to resolve.
*/
type TypeNodeToTypeStructureConsole = (
message: string,
failingTypeNode: TypeNode,
) => void;
declare function VoidTypeNodeToTypeStructureConsole(
message: string,
failingTypeNode: TypeNode
): void;
interface RootStructureWithConvertFailures<
TKind extends StructureKind = StructureKind,
>
{
rootStructure: Extract<StructureImpls, KindedStructure<TKind>>;
failures: readonly BuildTypesForStructureFailures[];
}
interface BuildTypesForStructureFailures {
message: string;
failingTypeNode: TypeNode;
}
All of these are exports from "ts-morph-structures"
:
userConsole
is a callback function for when ts-morph-structures might fail to convert a value. (If it does, please file a bug.)assertNoFailures
will cause te conversion to throw an exception if any conversion fails, recursively.kind
argument will force a check against the node you pass in for a matching syntax kind, and assert the returned structure is of the kind you pass in.rootStructure
of the getTypeAugmentedStructure()
output.Where you see a structure (where the name ends with Structure
), there is an equivalent structure implementation class (where the name ends with Impl
). For example:
// simplified for readability
declare class ClassDeclarationImpl {
static clone(source: OptionalKind<ClassDeclarationStructure>): ClassDeclarationImpl;
readonly kind: StructureKind.Class;
name?: string | undefined;
readonly leadingTrivia: (string | WriterFunction)[];
readonly trailingTrivia: (string | WriterFunction)[];
readonly decorators: DecoratorStructureImpl[];
readonly docs: (string | JSDocStructureImpl)[];
hasDeclareKeyword: boolean;
isAbstract: boolean;
isDefaultExport: boolean;
isExported: boolean;
readonly typeParameters: (string | TypeParameterDeclarationStructureImpl)[];
readonly ctors: ConstructorDeclarationImpl[] = []
readonly getAccessors: GetAccessorDeclarationImpl[] = []
readonly methods: MethodDeclarationImpl[] = [];
readonly properties: PropertyDeclarationImpl[] = []
readonly setAccessors: SetAccessorDeclarationImpl[] = [];
get extends(): stringOrWriterFunction | undefined;
set extends(value: stringOrWriterFunction | undefined);
get extendsStructure(): TypeStructures | undefined;
set extendsStructure(value: TypeStructures | undefined);
get implements(): stringOrWriterFunction[];
readonly implementsSet: TypeStructureSet;
toJSON(): StructureClassToJSON<ClassDeclarationImpl>;
}
So if I import ClassDeclarationImpl
and call new ClassDeclarationImpl
, all these fields are immediately available and well-defined.
Some of the structure classes have constructors to initialize required fields.
Generally speaking, where you have a single type field:
const method = new MethodSignatureDeclarationImpl("foo");
method.returnTypeStructure = LiteralTypeStructureImpl.get("boolean");
console.log(method.returnType); // writes "boolean", without the quotes.
Where you have an array of type structures (specifically, ClassDeclarationImpl::implements
and InterfaceDeclarationImpl::extends
), there are some special rules:
(string | WriterFunction)[]
.
TypeStructure
objects.ReadonlyArrayProxy
to control indexed access.So how do you modify the array of types?
These classes also provide type structure sets, in ClassDeclarationImpl::implementsSet
and InterfaceDeclarationImpl::extendsSet
, respectively. Each of these is a TypeStructuresSet object: a Set<TypeStructures>
with a couple extra methods:
cloneFromTypeStructureSet(other: TypeStructureSet): void;
replaceFromTypeArray(array: (string | WriterFunction)[]): void;
So you can just call classDecl.implementsSet.add(typeStructure);
, and it will update classDecl.implements
for you.
As for the type structure classes themselves, see the guide on type structures.
First, read ts-morph’s “setting with structure” documentation.
Each structure class instance should correctly implement a Structure
… if it doesn’t, file a bug on this project!