From 94ce873b53e1ee6a7ef03075131b6d7d51d41fc6 Mon Sep 17 00:00:00 2001 From: Dario <105294544+Dario-DC@users.noreply.github.com> Date: Mon, 9 Mar 2026 18:10:36 +0100 Subject: [PATCH 1/5] docs: add TS helpers --- src/content/docs/curriculum-help.mdx | 271 +++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) diff --git a/src/content/docs/curriculum-help.mdx b/src/content/docs/curriculum-help.mdx index c8e7334d..353ce299 100644 --- a/src/content/docs/curriculum-help.mdx +++ b/src/content/docs/curriculum-help.mdx @@ -1245,3 +1245,274 @@ def add( ): return x + y ``` + +## TypeScript Helpers + +These helpers provide Abstract Syntax Tree (AST) analysis capabilities for TypeScript challenges. + + +### Basic Usage + +`Explorer` is a chainable class that allows you to call methods on the result of parsing a string. Here's how to create an instance of `Explorer` that parses the camper's code: + +```js +const explorer = await __helpers.Explorer(code); +``` + +To access a specific statement within the code you need to chain method calls until you reach the desired scope. For example, if the camper has written the following code: + +```ts +class Spam { method(): number { return 42; } }; +``` + +To check the return type annotation you would write: + +```js +const explorer = await __helpers.Explorer(code); +assert.isTrue(explorer.getClasses().Spam.getMethods().method.hasReturnAnnotation("number")); +``` + +### Methods + +#### `isEmpty()` + +Returns `true` if the Explorer instance has no AST node, otherwise returns `false`. + +```js +const explorer = new Explorer("const a = 1;"); +explorer.isEmpty(); // false +``` + +#### `toString()` + +Returns the source code representation of the current node, or `"no ast"` if the node is empty. + +```js +const explorer = new Explorer("const a = 1;"); +explorer.toString(); // "const a = 1;" +``` + +#### `matches(other: string | Explorer)` + +Compares the current tree with another tree or string, ignoring semicolons and irrelevant whitespace differences. + +```js +const explorer = new Explorer("const a = 1;"); +explorer.matches("const a = 1"); // true (ignores whitespace and semicolons) +explorer.matches("const b = 1;"); // false +``` + +#### `getVariables()` + +Returns an object mapping variable names to `Explorer` instances for all variables in the current scope. + +```js +const explorer = new Explorer("var a = 1; let b = 2; const c = () => {}"); +const variables = explorer.getVariables(); // { a: Explorer, b: Explorer, c: Explorer } +const { a, b, c } = variables; +a.matches("var a = 1;"); // true +b.matches("let b = 1;"); // true +c.matches("const c = () => {}"); // true +``` + +#### `getValue()` + +Returns an `Explorer` instance representing the assigned value of a variable, property, parameter, or property assignment. Returns an empty `Explorer` if there is no initializer. + +```js +const explorer = new Explorer("const a = 1; const b = { x: 10 };"); +const { a, b } = explorer.getVariables(); +a.getValue().toString(); // "1" +b.getValue().matches("{ x: 10 }"); // true +``` + +#### `getObjectProps()` + +Returns an object mapping property names to `Explorer` instances for all properties in an object literal. + +```js +const explorer = new Explorer("const obj = { x: 1, y: 'hello', z: true };"); +const { obj } = explorer.getVariables(); +const props = obj.getValue().getObjectProps(); // { x: Explorer, y: Explorer, z: Explorer } +``` + +#### `getFunctions(withVariables?: boolean)` + +Returns an object mapping function names to `Explorer` instances; pass `true` to include arrow functions and function expressions assigned to variables. + +```js +const explorer = new Explorer("function foo() { return 42; } const bar = () => 24;"); +explorer.getFunctions(); // { foo: Explorer } +explorer.getFunctions(true); // { foo: Explorer, bar: Explorer } +``` + +#### `getParameters()` + +Returns an array of `Explorer` instances representing the parameters of a function or method. + +```js +const explorer = new Explorer("function foo(x: number, y: string) { return 42; }"); +const parameters = explorer.getFunctions().foo.getParameters(); +parameters[0].toString() // x: number +parameters[1].toString() // y: string +``` + +#### `getAnnotation()` + +Returns an `Explorer` instance representing the type annotation of the current node, or an empty `Explorer` if none exists. + +```js +const explorer = new Explorer("const a: number = 1; const b: { age: number } = { age: 33 }"); +const { a, b } = explorer.getVariables(); +a.getAnnotation(); // Explorer with "number" +b.getAnnotation(); // Explorer with "{ age: number }" +``` + +#### `hasAnnotation(annotation: string)` + +Returns `true` if the current node has a type annotation matching the specified string. + +```js +const explorer = new Explorer("const a: number = 1;"); +const { a } = explorer.getVariables(); +a.hasAnnotation("number"); // true +a.hasAnnotation("string"); // false +``` + +#### `hasReturnAnnotation(annotation: string)` + +Returns `true` if the function/method has a return type annotation matching the specified type. + +```js +const explorer = new Explorer("function foo(): number { return 42; }"); +const { foo } = explorer.getFunctions(); +foo.hasReturnAnnotation("number"); // true +foo.hasReturnAnnotation("string"); // false +``` + +#### `getTypes()` + +Returns an object mapping type alias names to `Explorer` instances for all type aliases in the current scope. + +```js +const explorer = new Explorer("type Foo = { x: number; }; type Bar = { y: string; };"); +const types = explorer.getTypes(); // { Foo: Explorer, Bar: Explorer } +types.Foo.matches("type Foo = { x: number; };"); // true +types.Bar.matches("type Bar = { y: string; };"); // true +``` + +#### `getInterfaces()` + +Returns an object mapping interface names to `Explorer` instances for all interfaces in the current scope. + +```js +const explorer = new Explorer("interface Foo { x: number; } interface Bar { y: string; }"); +const interfaces = explorer.getInterfaces(); // { Foo: Explorer, Bar: Explorer } +interfaces.Foo.matches("interface Foo { x: number; }"); // true +interfaces.Bar.matches("interface Bar { y: string; }"); // true +``` + +#### `getClasses()` + +Returns an object mapping class names to `Explorer` instances for all classes in the current scope. + +```js +const explorer = new Explorer("class Foo { x: number; } class Bar { y: string; }"); +const classes = explorer.getClasses(); // { Foo: Explorer, Bar: Explorer } +classes.Foo.matches("class Foo { x: number; }"); // true +classes.Bar.matches("class Bar { y: string; }"); // true +``` + +#### `getMethods()` + +Returns an object mapping method names to `Explorer` instances for all methods in the current class. + +```js +const explorer = new Explorer("class Foo { method1() {} method2() {} }"); +const classes = explorer.getClasses(); +classes.Foo.getMethods(); // { method1: Explorer, method2: Explorer } +``` + +#### `getClassProps()` + +Returns an object mapping property names to `Explorer` instances for all properties in the current class. + +```js +const explorer = new Explorer("class Foo { prop1: number; prop2: string; }"); +const classes = explorer.getClasses(); +classes.Foo.getClassProps(); // { prop1: Explorer, prop2: Explorer } +``` + +#### `getTypeProps()` + +Returns an object mapping property names to `Explorer` instances for all properties in a type, interface, or type literal. + +```js +const explorer = new Explorer("type Foo = { x: number; y: string; };"); +const types = explorer.getTypes(); +types.Foo.getTypeProps(); // { x: Explorer, y: Explorer } +``` + +#### `hasTypeProps(props: TypeProp | TypeProp[])` + +Returns `true` if all specified properties exist in a type, interface, or type literal, with optional type and optionality verification. `TypeProp` is an object with the form `{ name: string; type?: string; isOptional?: boolean }`. + +```js +const explorer = new Explorer("type Foo = { x: number; y: string; z?: boolean; };"); +const { Foo } = explorer.getTypes(); +Foo.hasTypeProps({ name: "x" }); // true +Foo.hasTypeProps([ + { name: "x", type: "number" }, + { name: "y", type: "string", isOptional: false }, + { name: "z", isOptional: true } +]); // true +Foo.hasTypeProps({ name: "a" }); // false +``` + +#### `isUnionOf(types: string[])` + +Returns `true` if the current node has a union type annotation that includes all specified types, ignoring order. + +```js +const explorer = new Explorer("const a: number | string | boolean;"); +const { a } = explorer.getVariables(); +a.getAnnotation().isUnionOf(["number", "string", "boolean"]); // true +a.getAnnotation().isUnionOf(["number", "string"]); // false +``` + +#### `hasCast(expectedType?: string)` + +Checks if the current node is a type assertion (cast using `as`). Optionally verify the cast type matches the provided type string. + +```js +const explorer = new Explorer("const a = 1 as number; const b = 2 as string;"); +const { a, b } = explorer.getVariables(); +a.getValue().hasCast(); // true +a.getValue().hasCast("number"); // true +a.getValue().hasCast("string"); // false +b.getValue().hasCast("string"); // true +``` + +#### `doesExtend(basesToCheck: string | string[])` + +Checks if a class or interface extends the specified base class or interface(s). Accepts a single string or an array of strings. + +```js +const explorer = new Explorer("class Foo extends Bar { }"); +const { Foo } = explorer.getClasses(); +Foo.doesExtend("Bar"); // true +Foo.doesExtend(["Bar", "Baz"]); // true if Bar or Baz is extended +Foo.doesExtend("Baz"); // false +``` + +#### `doesImplement(basesToCheck: string | string[])` + +Checks if a class implements the specified interface(s). Accepts a single string or an array of strings. + +```js +const explorer = new Explorer("class Foo implements Bar, Baz { }"); +const { Foo } = explorer.getClasses(); +Foo.doesImplement("Bar"); // true +Foo.doesImplement(["Bar", "Baz"]); // true +Foo.doesImplement("Spam"); // false +``` From f38fb108f812ca28141b762bcbf51b6c765ad3bb Mon Sep 17 00:00:00 2001 From: Dario <105294544+Dario-DC@users.noreply.github.com> Date: Mon, 9 Mar 2026 18:35:24 +0100 Subject: [PATCH 2/5] make linter happy --- src/content/docs/curriculum-help.mdx | 135 +++++++++++++++------------ 1 file changed, 77 insertions(+), 58 deletions(-) diff --git a/src/content/docs/curriculum-help.mdx b/src/content/docs/curriculum-help.mdx index 353ce299..801bb68e 100644 --- a/src/content/docs/curriculum-help.mdx +++ b/src/content/docs/curriculum-help.mdx @@ -1250,7 +1250,6 @@ def add( These helpers provide Abstract Syntax Tree (AST) analysis capabilities for TypeScript challenges. - ### Basic Usage `Explorer` is a chainable class that allows you to call methods on the result of parsing a string. Here's how to create an instance of `Explorer` that parses the camper's code: @@ -1262,14 +1261,20 @@ const explorer = await __helpers.Explorer(code); To access a specific statement within the code you need to chain method calls until you reach the desired scope. For example, if the camper has written the following code: ```ts -class Spam { method(): number { return 42; } }; +class Spam { + method(): number { + return 42; + } +} ``` To check the return type annotation you would write: ```js const explorer = await __helpers.Explorer(code); -assert.isTrue(explorer.getClasses().Spam.getMethods().method.hasReturnAnnotation("number")); +assert.isTrue( + explorer.getClasses().Spam.getMethods().method.hasReturnAnnotation('number') +); ``` ### Methods @@ -1279,7 +1284,7 @@ assert.isTrue(explorer.getClasses().Spam.getMethods().method.hasReturnAnnotation Returns `true` if the Explorer instance has no AST node, otherwise returns `false`. ```js -const explorer = new Explorer("const a = 1;"); +const explorer = new Explorer('const a = 1;'); explorer.isEmpty(); // false ``` @@ -1288,7 +1293,7 @@ explorer.isEmpty(); // false Returns the source code representation of the current node, or `"no ast"` if the node is empty. ```js -const explorer = new Explorer("const a = 1;"); +const explorer = new Explorer('const a = 1;'); explorer.toString(); // "const a = 1;" ``` @@ -1297,9 +1302,9 @@ explorer.toString(); // "const a = 1;" Compares the current tree with another tree or string, ignoring semicolons and irrelevant whitespace differences. ```js -const explorer = new Explorer("const a = 1;"); -explorer.matches("const a = 1"); // true (ignores whitespace and semicolons) -explorer.matches("const b = 1;"); // false +const explorer = new Explorer('const a = 1;'); +explorer.matches('const a = 1'); // true (ignores whitespace and semicolons) +explorer.matches('const b = 1;'); // false ``` #### `getVariables()` @@ -1307,12 +1312,12 @@ explorer.matches("const b = 1;"); // false Returns an object mapping variable names to `Explorer` instances for all variables in the current scope. ```js -const explorer = new Explorer("var a = 1; let b = 2; const c = () => {}"); +const explorer = new Explorer('var a = 1; let b = 2; const c = () => {}'); const variables = explorer.getVariables(); // { a: Explorer, b: Explorer, c: Explorer } const { a, b, c } = variables; -a.matches("var a = 1;"); // true -b.matches("let b = 1;"); // true -c.matches("const c = () => {}"); // true +a.matches('var a = 1;'); // true +b.matches('let b = 1;'); // true +c.matches('const c = () => {}'); // true ``` #### `getValue()` @@ -1320,10 +1325,10 @@ c.matches("const c = () => {}"); // true Returns an `Explorer` instance representing the assigned value of a variable, property, parameter, or property assignment. Returns an empty `Explorer` if there is no initializer. ```js -const explorer = new Explorer("const a = 1; const b = { x: 10 };"); +const explorer = new Explorer('const a = 1; const b = { x: 10 };'); const { a, b } = explorer.getVariables(); a.getValue().toString(); // "1" -b.getValue().matches("{ x: 10 }"); // true +b.getValue().matches('{ x: 10 }'); // true ``` #### `getObjectProps()` @@ -1341,7 +1346,9 @@ const props = obj.getValue().getObjectProps(); // { x: Explorer, y: Explorer, z: Returns an object mapping function names to `Explorer` instances; pass `true` to include arrow functions and function expressions assigned to variables. ```js -const explorer = new Explorer("function foo() { return 42; } const bar = () => 24;"); +const explorer = new Explorer( + 'function foo() { return 42; } const bar = () => 24;' +); explorer.getFunctions(); // { foo: Explorer } explorer.getFunctions(true); // { foo: Explorer, bar: Explorer } ``` @@ -1351,10 +1358,12 @@ explorer.getFunctions(true); // { foo: Explorer, bar: Explorer } Returns an array of `Explorer` instances representing the parameters of a function or method. ```js -const explorer = new Explorer("function foo(x: number, y: string) { return 42; }"); +const explorer = new Explorer( + 'function foo(x: number, y: string) { return 42; }' +); const parameters = explorer.getFunctions().foo.getParameters(); -parameters[0].toString() // x: number -parameters[1].toString() // y: string +parameters[0].toString(); // x: number +parameters[1].toString(); // y: string ``` #### `getAnnotation()` @@ -1362,7 +1371,9 @@ parameters[1].toString() // y: string Returns an `Explorer` instance representing the type annotation of the current node, or an empty `Explorer` if none exists. ```js -const explorer = new Explorer("const a: number = 1; const b: { age: number } = { age: 33 }"); +const explorer = new Explorer( + 'const a: number = 1; const b: { age: number } = { age: 33 }' +); const { a, b } = explorer.getVariables(); a.getAnnotation(); // Explorer with "number" b.getAnnotation(); // Explorer with "{ age: number }" @@ -1373,10 +1384,10 @@ b.getAnnotation(); // Explorer with "{ age: number }" Returns `true` if the current node has a type annotation matching the specified string. ```js -const explorer = new Explorer("const a: number = 1;"); +const explorer = new Explorer('const a: number = 1;'); const { a } = explorer.getVariables(); -a.hasAnnotation("number"); // true -a.hasAnnotation("string"); // false +a.hasAnnotation('number'); // true +a.hasAnnotation('string'); // false ``` #### `hasReturnAnnotation(annotation: string)` @@ -1384,10 +1395,10 @@ a.hasAnnotation("string"); // false Returns `true` if the function/method has a return type annotation matching the specified type. ```js -const explorer = new Explorer("function foo(): number { return 42; }"); +const explorer = new Explorer('function foo(): number { return 42; }'); const { foo } = explorer.getFunctions(); -foo.hasReturnAnnotation("number"); // true -foo.hasReturnAnnotation("string"); // false +foo.hasReturnAnnotation('number'); // true +foo.hasReturnAnnotation('string'); // false ``` #### `getTypes()` @@ -1395,10 +1406,12 @@ foo.hasReturnAnnotation("string"); // false Returns an object mapping type alias names to `Explorer` instances for all type aliases in the current scope. ```js -const explorer = new Explorer("type Foo = { x: number; }; type Bar = { y: string; };"); +const explorer = new Explorer( + 'type Foo = { x: number; }; type Bar = { y: string; };' +); const types = explorer.getTypes(); // { Foo: Explorer, Bar: Explorer } -types.Foo.matches("type Foo = { x: number; };"); // true -types.Bar.matches("type Bar = { y: string; };"); // true +types.Foo.matches('type Foo = { x: number; };'); // true +types.Bar.matches('type Bar = { y: string; };'); // true ``` #### `getInterfaces()` @@ -1406,10 +1419,12 @@ types.Bar.matches("type Bar = { y: string; };"); // true Returns an object mapping interface names to `Explorer` instances for all interfaces in the current scope. ```js -const explorer = new Explorer("interface Foo { x: number; } interface Bar { y: string; }"); +const explorer = new Explorer( + 'interface Foo { x: number; } interface Bar { y: string; }' +); const interfaces = explorer.getInterfaces(); // { Foo: Explorer, Bar: Explorer } -interfaces.Foo.matches("interface Foo { x: number; }"); // true -interfaces.Bar.matches("interface Bar { y: string; }"); // true +interfaces.Foo.matches('interface Foo { x: number; }'); // true +interfaces.Bar.matches('interface Bar { y: string; }'); // true ``` #### `getClasses()` @@ -1417,10 +1432,12 @@ interfaces.Bar.matches("interface Bar { y: string; }"); // true Returns an object mapping class names to `Explorer` instances for all classes in the current scope. ```js -const explorer = new Explorer("class Foo { x: number; } class Bar { y: string; }"); +const explorer = new Explorer( + 'class Foo { x: number; } class Bar { y: string; }' +); const classes = explorer.getClasses(); // { Foo: Explorer, Bar: Explorer } -classes.Foo.matches("class Foo { x: number; }"); // true -classes.Bar.matches("class Bar { y: string; }"); // true +classes.Foo.matches('class Foo { x: number; }'); // true +classes.Bar.matches('class Bar { y: string; }'); // true ``` #### `getMethods()` @@ -1428,7 +1445,7 @@ classes.Bar.matches("class Bar { y: string; }"); // true Returns an object mapping method names to `Explorer` instances for all methods in the current class. ```js -const explorer = new Explorer("class Foo { method1() {} method2() {} }"); +const explorer = new Explorer('class Foo { method1() {} method2() {} }'); const classes = explorer.getClasses(); classes.Foo.getMethods(); // { method1: Explorer, method2: Explorer } ``` @@ -1438,7 +1455,7 @@ classes.Foo.getMethods(); // { method1: Explorer, method2: Explorer } Returns an object mapping property names to `Explorer` instances for all properties in the current class. ```js -const explorer = new Explorer("class Foo { prop1: number; prop2: string; }"); +const explorer = new Explorer('class Foo { prop1: number; prop2: string; }'); const classes = explorer.getClasses(); classes.Foo.getClassProps(); // { prop1: Explorer, prop2: Explorer } ``` @@ -1448,7 +1465,7 @@ classes.Foo.getClassProps(); // { prop1: Explorer, prop2: Explorer } Returns an object mapping property names to `Explorer` instances for all properties in a type, interface, or type literal. ```js -const explorer = new Explorer("type Foo = { x: number; y: string; };"); +const explorer = new Explorer('type Foo = { x: number; y: string; };'); const types = explorer.getTypes(); types.Foo.getTypeProps(); // { x: Explorer, y: Explorer } ``` @@ -1458,15 +1475,17 @@ types.Foo.getTypeProps(); // { x: Explorer, y: Explorer } Returns `true` if all specified properties exist in a type, interface, or type literal, with optional type and optionality verification. `TypeProp` is an object with the form `{ name: string; type?: string; isOptional?: boolean }`. ```js -const explorer = new Explorer("type Foo = { x: number; y: string; z?: boolean; };"); +const explorer = new Explorer( + 'type Foo = { x: number; y: string; z?: boolean; };' +); const { Foo } = explorer.getTypes(); -Foo.hasTypeProps({ name: "x" }); // true +Foo.hasTypeProps({ name: 'x' }); // true Foo.hasTypeProps([ - { name: "x", type: "number" }, - { name: "y", type: "string", isOptional: false }, - { name: "z", isOptional: true } + { name: 'x', type: 'number' }, + { name: 'y', type: 'string', isOptional: false }, + { name: 'z', isOptional: true } ]); // true -Foo.hasTypeProps({ name: "a" }); // false +Foo.hasTypeProps({ name: 'a' }); // false ``` #### `isUnionOf(types: string[])` @@ -1474,10 +1493,10 @@ Foo.hasTypeProps({ name: "a" }); // false Returns `true` if the current node has a union type annotation that includes all specified types, ignoring order. ```js -const explorer = new Explorer("const a: number | string | boolean;"); +const explorer = new Explorer('const a: number | string | boolean;'); const { a } = explorer.getVariables(); -a.getAnnotation().isUnionOf(["number", "string", "boolean"]); // true -a.getAnnotation().isUnionOf(["number", "string"]); // false +a.getAnnotation().isUnionOf(['number', 'string', 'boolean']); // true +a.getAnnotation().isUnionOf(['number', 'string']); // false ``` #### `hasCast(expectedType?: string)` @@ -1485,12 +1504,12 @@ a.getAnnotation().isUnionOf(["number", "string"]); // false Checks if the current node is a type assertion (cast using `as`). Optionally verify the cast type matches the provided type string. ```js -const explorer = new Explorer("const a = 1 as number; const b = 2 as string;"); +const explorer = new Explorer('const a = 1 as number; const b = 2 as string;'); const { a, b } = explorer.getVariables(); a.getValue().hasCast(); // true -a.getValue().hasCast("number"); // true -a.getValue().hasCast("string"); // false -b.getValue().hasCast("string"); // true +a.getValue().hasCast('number'); // true +a.getValue().hasCast('string'); // false +b.getValue().hasCast('string'); // true ``` #### `doesExtend(basesToCheck: string | string[])` @@ -1498,11 +1517,11 @@ b.getValue().hasCast("string"); // true Checks if a class or interface extends the specified base class or interface(s). Accepts a single string or an array of strings. ```js -const explorer = new Explorer("class Foo extends Bar { }"); +const explorer = new Explorer('class Foo extends Bar { }'); const { Foo } = explorer.getClasses(); -Foo.doesExtend("Bar"); // true -Foo.doesExtend(["Bar", "Baz"]); // true if Bar or Baz is extended -Foo.doesExtend("Baz"); // false +Foo.doesExtend('Bar'); // true +Foo.doesExtend(['Bar', 'Baz']); // true if Bar or Baz is extended +Foo.doesExtend('Baz'); // false ``` #### `doesImplement(basesToCheck: string | string[])` @@ -1510,9 +1529,9 @@ Foo.doesExtend("Baz"); // false Checks if a class implements the specified interface(s). Accepts a single string or an array of strings. ```js -const explorer = new Explorer("class Foo implements Bar, Baz { }"); +const explorer = new Explorer('class Foo implements Bar, Baz { }'); const { Foo } = explorer.getClasses(); -Foo.doesImplement("Bar"); // true -Foo.doesImplement(["Bar", "Baz"]); // true -Foo.doesImplement("Spam"); // false +Foo.doesImplement('Bar'); // true +Foo.doesImplement(['Bar', 'Baz']); // true +Foo.doesImplement('Spam'); // false ``` From 64ecfe3e5c8762ab2e2a31e382bfba72ce7fe51f Mon Sep 17 00:00:00 2001 From: Dario <105294544+Dario-DC@users.noreply.github.com> Date: Fri, 13 Mar 2026 11:02:25 +0100 Subject: [PATCH 3/5] update to reflect API changes --- src/content/docs/curriculum-help.mdx | 157 ++++++++++++++++++--------- 1 file changed, 103 insertions(+), 54 deletions(-) diff --git a/src/content/docs/curriculum-help.mdx b/src/content/docs/curriculum-help.mdx index 801bb68e..ef08b014 100644 --- a/src/content/docs/curriculum-help.mdx +++ b/src/content/docs/curriculum-help.mdx @@ -1273,11 +1273,11 @@ To check the return type annotation you would write: ```js const explorer = await __helpers.Explorer(code); assert.isTrue( - explorer.getClasses().Spam.getMethods().method.hasReturnAnnotation('number') + explorer.classes.Spam.methods.method.hasReturnAnnotation('number') ); ``` -### Methods +### Methods and Properties #### `isEmpty()` @@ -1307,53 +1307,52 @@ explorer.matches('const a = 1'); // true (ignores whitespace and semicolons) explorer.matches('const b = 1;'); // false ``` -#### `getVariables()` +#### `variables` Returns an object mapping variable names to `Explorer` instances for all variables in the current scope. ```js const explorer = new Explorer('var a = 1; let b = 2; const c = () => {}'); -const variables = explorer.getVariables(); // { a: Explorer, b: Explorer, c: Explorer } -const { a, b, c } = variables; +const { a, b, c } = explorer.variables; // { a: Explorer, b: Explorer, c: Explorer } a.matches('var a = 1;'); // true -b.matches('let b = 1;'); // true +b.matches('let b = 2;'); // true c.matches('const c = () => {}'); // true ``` -#### `getValue()` +#### `value` Returns an `Explorer` instance representing the assigned value of a variable, property, parameter, or property assignment. Returns an empty `Explorer` if there is no initializer. ```js const explorer = new Explorer('const a = 1; const b = { x: 10 };'); -const { a, b } = explorer.getVariables(); -a.getValue().toString(); // "1" -b.getValue().matches('{ x: 10 }'); // true +const { a, b } = explorer.variables; +a.value.toString(); // "1" +b.value.matches('{ x: 10 }'); // true ``` -#### `getObjectProps()` +#### `objectProps` Returns an object mapping property names to `Explorer` instances for all properties in an object literal. ```js const explorer = new Explorer("const obj = { x: 1, y: 'hello', z: true };"); -const { obj } = explorer.getVariables(); -const props = obj.getValue().getObjectProps(); // { x: Explorer, y: Explorer, z: Explorer } +const { obj } = explorer.variables; +const props = obj.value.objectProps; // { x: Explorer, y: Explorer, z: Explorer } ``` -#### `getFunctions(withVariables?: boolean)` +#### `functions` and `allFunctions` -Returns an object mapping function names to `Explorer` instances; pass `true` to include arrow functions and function expressions assigned to variables. +`functions` returns function declarations only. `allFunctions` also includes arrow functions and function expressions assigned to variables. ```js const explorer = new Explorer( 'function foo() { return 42; } const bar = () => 24;' ); -explorer.getFunctions(); // { foo: Explorer } -explorer.getFunctions(true); // { foo: Explorer, bar: Explorer } +explorer.functions; // { foo: Explorer } +explorer.allFunctions; // { foo: Explorer, bar: Explorer } ``` -#### `getParameters()` +#### `parameters` Returns an array of `Explorer` instances representing the parameters of a function or method. @@ -1361,12 +1360,12 @@ Returns an array of `Explorer` instances representing the parameters of a functi const explorer = new Explorer( 'function foo(x: number, y: string) { return 42; }' ); -const parameters = explorer.getFunctions().foo.getParameters(); +const parameters = explorer.functions.foo.parameters; parameters[0].toString(); // x: number parameters[1].toString(); // y: string ``` -#### `getAnnotation()` +#### `annotation` Returns an `Explorer` instance representing the type annotation of the current node, or an empty `Explorer` if none exists. @@ -1374,9 +1373,9 @@ Returns an `Explorer` instance representing the type annotation of the current n const explorer = new Explorer( 'const a: number = 1; const b: { age: number } = { age: 33 }' ); -const { a, b } = explorer.getVariables(); -a.getAnnotation(); // Explorer with "number" -b.getAnnotation(); // Explorer with "{ age: number }" +const { a, b } = explorer.variables; +a.annotation; // Explorer with "number" +b.annotation; // Explorer with "{ age: number }" ``` #### `hasAnnotation(annotation: string)` @@ -1385,7 +1384,7 @@ Returns `true` if the current node has a type annotation matching the specified ```js const explorer = new Explorer('const a: number = 1;'); -const { a } = explorer.getVariables(); +const { a } = explorer.variables; a.hasAnnotation('number'); // true a.hasAnnotation('string'); // false ``` @@ -1396,12 +1395,22 @@ Returns `true` if the function/method has a return type annotation matching the ```js const explorer = new Explorer('function foo(): number { return 42; }'); -const { foo } = explorer.getFunctions(); +const { foo } = explorer.functions; foo.hasReturnAnnotation('number'); // true foo.hasReturnAnnotation('string'); // false ``` -#### `getTypes()` +#### `hasReturn(value: string)` + +Checks whether a function, method, or function-valued variable has a matching top-level return expression. + +```js +const explorer = new Explorer('function foo() { return 42; }'); +const { foo } = explorer.functions; +foo.hasReturn('42'); // true +``` + +#### `types` Returns an object mapping type alias names to `Explorer` instances for all type aliases in the current scope. @@ -1409,12 +1418,12 @@ Returns an object mapping type alias names to `Explorer` instances for all type const explorer = new Explorer( 'type Foo = { x: number; }; type Bar = { y: string; };' ); -const types = explorer.getTypes(); // { Foo: Explorer, Bar: Explorer } +const { types } = explorer; // { Foo: Explorer, Bar: Explorer } types.Foo.matches('type Foo = { x: number; };'); // true types.Bar.matches('type Bar = { y: string; };'); // true ``` -#### `getInterfaces()` +#### `interfaces` Returns an object mapping interface names to `Explorer` instances for all interfaces in the current scope. @@ -1422,12 +1431,12 @@ Returns an object mapping interface names to `Explorer` instances for all interf const explorer = new Explorer( 'interface Foo { x: number; } interface Bar { y: string; }' ); -const interfaces = explorer.getInterfaces(); // { Foo: Explorer, Bar: Explorer } +const { interfaces } = explorer; // { Foo: Explorer, Bar: Explorer } interfaces.Foo.matches('interface Foo { x: number; }'); // true interfaces.Bar.matches('interface Bar { y: string; }'); // true ``` -#### `getClasses()` +#### `classes` Returns an object mapping class names to `Explorer` instances for all classes in the current scope. @@ -1435,39 +1444,43 @@ Returns an object mapping class names to `Explorer` instances for all classes in const explorer = new Explorer( 'class Foo { x: number; } class Bar { y: string; }' ); -const classes = explorer.getClasses(); // { Foo: Explorer, Bar: Explorer } +const { classes } = explorer; // { Foo: Explorer, Bar: Explorer } classes.Foo.matches('class Foo { x: number; }'); // true classes.Bar.matches('class Bar { y: string; }'); // true ``` -#### `getMethods()` +#### `methods` Returns an object mapping method names to `Explorer` instances for all methods in the current class. ```js const explorer = new Explorer('class Foo { method1() {} method2() {} }'); -const classes = explorer.getClasses(); -classes.Foo.getMethods(); // { method1: Explorer, method2: Explorer } +const { Foo } = explorer.classes; +const { method1, method2 } = Foo.methods; // { method1: Explorer, method2: Explorer } ``` -#### `getClassProps()` +#### `classConstructor`, `classProps`, and `constructorProps` -Returns an object mapping property names to `Explorer` instances for all properties in the current class. +Use these to inspect class constructors and class-related properties. ```js -const explorer = new Explorer('class Foo { prop1: number; prop2: string; }'); -const classes = explorer.getClasses(); -classes.Foo.getClassProps(); // { prop1: Explorer, prop2: Explorer } +const explorer = new Explorer( + 'class Foo { prop1: number; constructor(x) { this.y = x; } }' +); +const { Foo } = explorer.classes; +Foo.classConstructor?.matches('constructor(x) { this.y = x; }'); // true +Foo.classProps; // { prop1: Explorer } +Foo.constructorProps; // { y: Explorer } ``` -#### `getTypeProps()` +#### `typeProps` Returns an object mapping property names to `Explorer` instances for all properties in a type, interface, or type literal. ```js const explorer = new Explorer('type Foo = { x: number; y: string; };'); -const types = explorer.getTypes(); -types.Foo.getTypeProps(); // { x: Explorer, y: Explorer } +const { Foo } = explorer.types; +const { x, y } = Foo.typeProps; // { x: Explorer, y: Explorer } ``` #### `hasTypeProps(props: TypeProp | TypeProp[])` @@ -1478,7 +1491,7 @@ Returns `true` if all specified properties exist in a type, interface, or type l const explorer = new Explorer( 'type Foo = { x: number; y: string; z?: boolean; };' ); -const { Foo } = explorer.getTypes(); +const { Foo } = explorer.types; Foo.hasTypeProps({ name: 'x' }); // true Foo.hasTypeProps([ { name: 'x', type: 'number' }, @@ -1494,9 +1507,9 @@ Returns `true` if the current node has a union type annotation that includes all ```js const explorer = new Explorer('const a: number | string | boolean;'); -const { a } = explorer.getVariables(); -a.getAnnotation().isUnionOf(['number', 'string', 'boolean']); // true -a.getAnnotation().isUnionOf(['number', 'string']); // false +const { a } = explorer.variables; +a.annotation.isUnionOf(['number', 'string', 'boolean']); // true +a.annotation.isUnionOf(['number', 'string']); // false ``` #### `hasCast(expectedType?: string)` @@ -1505,11 +1518,21 @@ Checks if the current node is a type assertion (cast using `as`). Optionally ver ```js const explorer = new Explorer('const a = 1 as number; const b = 2 as string;'); -const { a, b } = explorer.getVariables(); -a.getValue().hasCast(); // true -a.getValue().hasCast('number'); // true -a.getValue().hasCast('string'); // false -b.getValue().hasCast('string'); // true +const { a, b } = explorer.variables; +a.value.hasCast(); // true +a.value.hasCast('number'); // true +a.value.hasCast('string'); // false +b.value.hasCast('string'); // true +``` + +#### `hasNonNullAssertion()` + +Checks whether the current expression is a non-null assertion (`!`). + +```js +const explorer = new Explorer('const a = maybeValue!;'); +const { a } = explorer.variables; +a.value.hasNonNullAssertion(); // true ``` #### `doesExtend(basesToCheck: string | string[])` @@ -1518,9 +1541,9 @@ Checks if a class or interface extends the specified base class or interface(s). ```js const explorer = new Explorer('class Foo extends Bar { }'); -const { Foo } = explorer.getClasses(); +const { Foo } = explorer.classes; Foo.doesExtend('Bar'); // true -Foo.doesExtend(['Bar', 'Baz']); // true if Bar or Baz is extended +Foo.doesExtend(['Bar', 'Baz']); // true only if both are extended Foo.doesExtend('Baz'); // false ``` @@ -1530,8 +1553,34 @@ Checks if a class implements the specified interface(s). Accepts a single string ```js const explorer = new Explorer('class Foo implements Bar, Baz { }'); -const { Foo } = explorer.getClasses(); +const { Foo } = explorer.classes; Foo.doesImplement('Bar'); // true Foo.doesImplement(['Bar', 'Baz']); // true Foo.doesImplement('Spam'); // false ``` + +#### `typeParameters` and `typeArguments` + +`typeParameters` returns generic declarations (like ``), and `typeArguments` returns generic usages (like ``). + +```js +const explorer = new Explorer('function id(x: T): T { return x; }'); +const { id } = explorer.functions; +id.typeParameters[0].matches('T'); // true + +const explorer2 = new Explorer('const m: Map = new Map();'); +const { m } = explorer2.variables; +m.annotation.typeArguments[0].matches('string'); // true +``` + +#### Access modifiers + +Use `isPrivate()`, `isProtected()`, `isPublic()`, and `isReadOnly()` on class members and type properties. + +```js +const explorer = new Explorer('class Foo { private readonly x: number; }'); +const { Foo } = explorer.classes; +const { x } = Foo.classProps; +x.isPrivate(); // true +x.isReadOnly(); // true +``` From 946c986325c6db2135ebcb3452e3ec5adca05db0 Mon Sep 17 00:00:00 2001 From: Dario <105294544+Dario-DC@users.noreply.github.com> Date: Fri, 13 Mar 2026 17:32:51 +0100 Subject: [PATCH 4/5] implement suggestions --- src/content/docs/curriculum-help.mdx | 80 +++++++++++++++++----------- 1 file changed, 50 insertions(+), 30 deletions(-) diff --git a/src/content/docs/curriculum-help.mdx b/src/content/docs/curriculum-help.mdx index ef08b014..7fcf5842 100644 --- a/src/content/docs/curriculum-help.mdx +++ b/src/content/docs/curriculum-help.mdx @@ -1284,7 +1284,7 @@ assert.isTrue( Returns `true` if the Explorer instance has no AST node, otherwise returns `false`. ```js -const explorer = new Explorer('const a = 1;'); +const explorer = await __helpers.Explorer('const a = 1;'); explorer.isEmpty(); // false ``` @@ -1293,7 +1293,7 @@ explorer.isEmpty(); // false Returns the source code representation of the current node, or `"no ast"` if the node is empty. ```js -const explorer = new Explorer('const a = 1;'); +const explorer = await __helpers.Explorer('const a = 1;'); explorer.toString(); // "const a = 1;" ``` @@ -1302,7 +1302,7 @@ explorer.toString(); // "const a = 1;" Compares the current tree with another tree or string, ignoring semicolons and irrelevant whitespace differences. ```js -const explorer = new Explorer('const a = 1;'); +const explorer = await __helpers.Explorer('const a = 1;'); explorer.matches('const a = 1'); // true (ignores whitespace and semicolons) explorer.matches('const b = 1;'); // false ``` @@ -1312,7 +1312,9 @@ explorer.matches('const b = 1;'); // false Returns an object mapping variable names to `Explorer` instances for all variables in the current scope. ```js -const explorer = new Explorer('var a = 1; let b = 2; const c = () => {}'); +const explorer = await __helpers.Explorer( + 'var a = 1; let b = 2; const c = () => {}' +); const { a, b, c } = explorer.variables; // { a: Explorer, b: Explorer, c: Explorer } a.matches('var a = 1;'); // true b.matches('let b = 2;'); // true @@ -1324,7 +1326,7 @@ c.matches('const c = () => {}'); // true Returns an `Explorer` instance representing the assigned value of a variable, property, parameter, or property assignment. Returns an empty `Explorer` if there is no initializer. ```js -const explorer = new Explorer('const a = 1; const b = { x: 10 };'); +const explorer = await __helpers.Explorer('const a = 1; const b = { x: 10 };'); const { a, b } = explorer.variables; a.value.toString(); // "1" b.value.matches('{ x: 10 }'); // true @@ -1335,7 +1337,9 @@ b.value.matches('{ x: 10 }'); // true Returns an object mapping property names to `Explorer` instances for all properties in an object literal. ```js -const explorer = new Explorer("const obj = { x: 1, y: 'hello', z: true };"); +const explorer = await __helpers.Explorer( + "const obj = { x: 1, y: 'hello', z: true };" +); const { obj } = explorer.variables; const props = obj.value.objectProps; // { x: Explorer, y: Explorer, z: Explorer } ``` @@ -1345,7 +1349,7 @@ const props = obj.value.objectProps; // { x: Explorer, y: Explorer, z: Explorer `functions` returns function declarations only. `allFunctions` also includes arrow functions and function expressions assigned to variables. ```js -const explorer = new Explorer( +const explorer = await __helpers.Explorer( 'function foo() { return 42; } const bar = () => 24;' ); explorer.functions; // { foo: Explorer } @@ -1357,7 +1361,7 @@ explorer.allFunctions; // { foo: Explorer, bar: Explorer } Returns an array of `Explorer` instances representing the parameters of a function or method. ```js -const explorer = new Explorer( +const explorer = await __helpers.Explorer( 'function foo(x: number, y: string) { return 42; }' ); const parameters = explorer.functions.foo.parameters; @@ -1370,7 +1374,7 @@ parameters[1].toString(); // y: string Returns an `Explorer` instance representing the type annotation of the current node, or an empty `Explorer` if none exists. ```js -const explorer = new Explorer( +const explorer = await __helpers.Explorer( 'const a: number = 1; const b: { age: number } = { age: 33 }' ); const { a, b } = explorer.variables; @@ -1383,7 +1387,7 @@ b.annotation; // Explorer with "{ age: number }" Returns `true` if the current node has a type annotation matching the specified string. ```js -const explorer = new Explorer('const a: number = 1;'); +const explorer = await __helpers.Explorer('const a: number = 1;'); const { a } = explorer.variables; a.hasAnnotation('number'); // true a.hasAnnotation('string'); // false @@ -1394,7 +1398,9 @@ a.hasAnnotation('string'); // false Returns `true` if the function/method has a return type annotation matching the specified type. ```js -const explorer = new Explorer('function foo(): number { return 42; }'); +const explorer = await __helpers.Explorer( + 'function foo(): number { return 42; }' +); const { foo } = explorer.functions; foo.hasReturnAnnotation('number'); // true foo.hasReturnAnnotation('string'); // false @@ -1405,7 +1411,7 @@ foo.hasReturnAnnotation('string'); // false Checks whether a function, method, or function-valued variable has a matching top-level return expression. ```js -const explorer = new Explorer('function foo() { return 42; }'); +const explorer = await __helpers.Explorer('function foo() { return 42; }'); const { foo } = explorer.functions; foo.hasReturn('42'); // true ``` @@ -1415,7 +1421,7 @@ foo.hasReturn('42'); // true Returns an object mapping type alias names to `Explorer` instances for all type aliases in the current scope. ```js -const explorer = new Explorer( +const explorer = await __helpers.Explorer( 'type Foo = { x: number; }; type Bar = { y: string; };' ); const { types } = explorer; // { Foo: Explorer, Bar: Explorer } @@ -1428,7 +1434,7 @@ types.Bar.matches('type Bar = { y: string; };'); // true Returns an object mapping interface names to `Explorer` instances for all interfaces in the current scope. ```js -const explorer = new Explorer( +const explorer = await __helpers.Explorer( 'interface Foo { x: number; } interface Bar { y: string; }' ); const { interfaces } = explorer; // { Foo: Explorer, Bar: Explorer } @@ -1441,7 +1447,7 @@ interfaces.Bar.matches('interface Bar { y: string; }'); // true Returns an object mapping class names to `Explorer` instances for all classes in the current scope. ```js -const explorer = new Explorer( +const explorer = await __helpers.Explorer( 'class Foo { x: number; } class Bar { y: string; }' ); const { classes } = explorer; // { Foo: Explorer, Bar: Explorer } @@ -1454,7 +1460,9 @@ classes.Bar.matches('class Bar { y: string; }'); // true Returns an object mapping method names to `Explorer` instances for all methods in the current class. ```js -const explorer = new Explorer('class Foo { method1() {} method2() {} }'); +const explorer = await __helpers.Explorer( + 'class Foo { method1() {} method2() {} }' +); const { Foo } = explorer.classes; const { method1, method2 } = Foo.methods; // { method1: Explorer, method2: Explorer } ``` @@ -1464,7 +1472,7 @@ const { method1, method2 } = Foo.methods; // { method1: Explorer, method2: Explo Use these to inspect class constructors and class-related properties. ```js -const explorer = new Explorer( +const explorer = await __helpers.Explorer( 'class Foo { prop1: number; constructor(x) { this.y = x; } }' ); const { Foo } = explorer.classes; @@ -1478,7 +1486,9 @@ Foo.constructorProps; // { y: Explorer } Returns an object mapping property names to `Explorer` instances for all properties in a type, interface, or type literal. ```js -const explorer = new Explorer('type Foo = { x: number; y: string; };'); +const explorer = await __helpers.Explorer( + 'type Foo = { x: number; y: string; };' +); const { Foo } = explorer.types; const { x, y } = Foo.typeProps; // { x: Explorer, y: Explorer } ``` @@ -1488,7 +1498,7 @@ const { x, y } = Foo.typeProps; // { x: Explorer, y: Explorer } Returns `true` if all specified properties exist in a type, interface, or type literal, with optional type and optionality verification. `TypeProp` is an object with the form `{ name: string; type?: string; isOptional?: boolean }`. ```js -const explorer = new Explorer( +const explorer = await __helpers.Explorer( 'type Foo = { x: number; y: string; z?: boolean; };' ); const { Foo } = explorer.types; @@ -1503,10 +1513,12 @@ Foo.hasTypeProps({ name: 'a' }); // false #### `isUnionOf(types: string[])` -Returns `true` if the current node has a union type annotation that includes all specified types, ignoring order. +Returns `true` if the current node has a union type annotation whose members exactly match the specified types, ignoring order. ```js -const explorer = new Explorer('const a: number | string | boolean;'); +const explorer = await __helpers.Explorer( + 'const a: number | string | boolean;' +); const { a } = explorer.variables; a.annotation.isUnionOf(['number', 'string', 'boolean']); // true a.annotation.isUnionOf(['number', 'string']); // false @@ -1517,7 +1529,9 @@ a.annotation.isUnionOf(['number', 'string']); // false Checks if the current node is a type assertion (cast using `as`). Optionally verify the cast type matches the provided type string. ```js -const explorer = new Explorer('const a = 1 as number; const b = 2 as string;'); +const explorer = await __helpers.Explorer( + 'const a = 1 as number; const b = 2 as string;' +); const { a, b } = explorer.variables; a.value.hasCast(); // true a.value.hasCast('number'); // true @@ -1530,7 +1544,7 @@ b.value.hasCast('string'); // true Checks whether the current expression is a non-null assertion (`!`). ```js -const explorer = new Explorer('const a = maybeValue!;'); +const explorer = await __helpers.Explorer('const a = maybeValue!;'); const { a } = explorer.variables; a.value.hasNonNullAssertion(); // true ``` @@ -1540,11 +1554,11 @@ a.value.hasNonNullAssertion(); // true Checks if a class or interface extends the specified base class or interface(s). Accepts a single string or an array of strings. ```js -const explorer = new Explorer('class Foo extends Bar { }'); -const { Foo } = explorer.classes; +const explorer = new Explorer('interface Foo extends Bar, Baz { }'); +const { Foo } = explorer.interfaces; Foo.doesExtend('Bar'); // true Foo.doesExtend(['Bar', 'Baz']); // true only if both are extended -Foo.doesExtend('Baz'); // false +Foo.doesExtend('Spam'); // false ``` #### `doesImplement(basesToCheck: string | string[])` @@ -1552,7 +1566,7 @@ Foo.doesExtend('Baz'); // false Checks if a class implements the specified interface(s). Accepts a single string or an array of strings. ```js -const explorer = new Explorer('class Foo implements Bar, Baz { }'); +const explorer = await __helpers.Explorer('class Foo implements Bar, Baz { }'); const { Foo } = explorer.classes; Foo.doesImplement('Bar'); // true Foo.doesImplement(['Bar', 'Baz']); // true @@ -1564,11 +1578,15 @@ Foo.doesImplement('Spam'); // false `typeParameters` returns generic declarations (like ``), and `typeArguments` returns generic usages (like ``). ```js -const explorer = new Explorer('function id(x: T): T { return x; }'); +const explorer = await __helpers.Explorer( + 'function id(x: T): T { return x; }' +); const { id } = explorer.functions; id.typeParameters[0].matches('T'); // true -const explorer2 = new Explorer('const m: Map = new Map();'); +const explorer2 = await __helpers.Explorer( + 'const m: Map = new Map();' +); const { m } = explorer2.variables; m.annotation.typeArguments[0].matches('string'); // true ``` @@ -1578,7 +1596,9 @@ m.annotation.typeArguments[0].matches('string'); // true Use `isPrivate()`, `isProtected()`, `isPublic()`, and `isReadOnly()` on class members and type properties. ```js -const explorer = new Explorer('class Foo { private readonly x: number; }'); +const explorer = await __helpers.Explorer( + 'class Foo { private readonly x: number; }' +); const { Foo } = explorer.classes; const { x } = Foo.classProps; x.isPrivate(); // true From 33c0255b075636ca9b5991e078b871d0347034f9 Mon Sep 17 00:00:00 2001 From: Dario <105294544+Dario-DC@users.noreply.github.com> Date: Fri, 13 Mar 2026 17:37:18 +0100 Subject: [PATCH 5/5] update code example --- src/content/docs/curriculum-help.mdx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/content/docs/curriculum-help.mdx b/src/content/docs/curriculum-help.mdx index 7fcf5842..a84badee 100644 --- a/src/content/docs/curriculum-help.mdx +++ b/src/content/docs/curriculum-help.mdx @@ -1262,7 +1262,7 @@ To access a specific statement within the code you need to chain method calls un ```ts class Spam { - method(): number { + get42(): number { return 42; } } @@ -1273,7 +1273,7 @@ To check the return type annotation you would write: ```js const explorer = await __helpers.Explorer(code); assert.isTrue( - explorer.classes.Spam.methods.method.hasReturnAnnotation('number') + explorer.classes.Spam.methods.get42.hasReturnAnnotation('number') ); ```