generated from freeCodeCamp/template
-
-
Notifications
You must be signed in to change notification settings - Fork 328
docs: add TS helpers #1236
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Dario-DC
wants to merge
5
commits into
freeCodeCamp:main
Choose a base branch
from
Dario-DC:add-ts-helpers-docs
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
docs: add TS helpers #1236
Changes from all commits
Commits
Show all changes
5 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1245,3 +1245,362 @@ 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 { | ||
| get42(): number { | ||
| return 42; | ||
| } | ||
| } | ||
| ``` | ||
|
|
||
| To check the return type annotation you would write: | ||
|
|
||
| ```js | ||
| const explorer = await __helpers.Explorer(code); | ||
| assert.isTrue( | ||
| explorer.classes.Spam.methods.get42.hasReturnAnnotation('number') | ||
| ); | ||
| ``` | ||
|
|
||
| ### Methods and Properties | ||
|
|
||
| #### `isEmpty()` | ||
|
|
||
| Returns `true` if the Explorer instance has no AST node, otherwise returns `false`. | ||
|
|
||
| ```js | ||
| const explorer = await __helpers.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 = await __helpers.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 = await __helpers.Explorer('const a = 1;'); | ||
| explorer.matches('const a = 1'); // true (ignores whitespace and semicolons) | ||
| explorer.matches('const b = 1;'); // false | ||
| ``` | ||
|
|
||
| #### `variables` | ||
|
|
||
| Returns an object mapping variable names to `Explorer` instances for all variables in the current scope. | ||
|
|
||
| ```js | ||
| 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 | ||
| c.matches('const c = () => {}'); // true | ||
| ``` | ||
|
|
||
| #### `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 = 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 | ||
| ``` | ||
|
|
||
| #### `objectProps` | ||
|
|
||
| Returns an object mapping property names to `Explorer` instances for all properties in an object literal. | ||
|
|
||
| ```js | ||
| 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 } | ||
| ``` | ||
|
|
||
| #### `functions` and `allFunctions` | ||
|
|
||
| `functions` returns function declarations only. `allFunctions` also includes arrow functions and function expressions assigned to variables. | ||
|
|
||
| ```js | ||
| const explorer = await __helpers.Explorer( | ||
| 'function foo() { return 42; } const bar = () => 24;' | ||
| ); | ||
| explorer.functions; // { foo: Explorer } | ||
| explorer.allFunctions; // { foo: Explorer, bar: Explorer } | ||
| ``` | ||
|
|
||
| #### `parameters` | ||
|
|
||
| Returns an array of `Explorer` instances representing the parameters of a function or method. | ||
|
|
||
| ```js | ||
| const explorer = await __helpers.Explorer( | ||
| 'function foo(x: number, y: string) { return 42; }' | ||
| ); | ||
| const parameters = explorer.functions.foo.parameters; | ||
| parameters[0].toString(); // x: number | ||
| parameters[1].toString(); // y: string | ||
| ``` | ||
|
|
||
| #### `annotation` | ||
|
|
||
| Returns an `Explorer` instance representing the type annotation of the current node, or an empty `Explorer` if none exists. | ||
|
|
||
| ```js | ||
| const explorer = await __helpers.Explorer( | ||
| 'const a: number = 1; const b: { age: number } = { age: 33 }' | ||
| ); | ||
| const { a, b } = explorer.variables; | ||
| a.annotation; // Explorer with "number" | ||
| b.annotation; // Explorer with "{ age: number }" | ||
| ``` | ||
|
|
||
| #### `hasAnnotation(annotation: string)` | ||
|
|
||
| Returns `true` if the current node has a type annotation matching the specified string. | ||
|
|
||
| ```js | ||
| const explorer = await __helpers.Explorer('const a: number = 1;'); | ||
| const { a } = explorer.variables; | ||
| 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 = await __helpers.Explorer( | ||
| 'function foo(): number { return 42; }' | ||
| ); | ||
| const { foo } = explorer.functions; | ||
| foo.hasReturnAnnotation('number'); // true | ||
| foo.hasReturnAnnotation('string'); // false | ||
| ``` | ||
|
|
||
| #### `hasReturn(value: string)` | ||
|
|
||
| Checks whether a function, method, or function-valued variable has a matching top-level return expression. | ||
|
|
||
| ```js | ||
| const explorer = await __helpers.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. | ||
|
|
||
| ```js | ||
| const explorer = await __helpers.Explorer( | ||
| 'type Foo = { x: number; }; type Bar = { y: string; };' | ||
| ); | ||
| const { types } = explorer; // { Foo: Explorer, Bar: Explorer } | ||
| types.Foo.matches('type Foo = { x: number; };'); // true | ||
| types.Bar.matches('type Bar = { y: string; };'); // true | ||
| ``` | ||
|
|
||
| #### `interfaces` | ||
|
|
||
| Returns an object mapping interface names to `Explorer` instances for all interfaces in the current scope. | ||
|
|
||
| ```js | ||
| const explorer = await __helpers.Explorer( | ||
| 'interface Foo { x: number; } interface Bar { y: string; }' | ||
| ); | ||
| const { interfaces } = explorer; // { Foo: Explorer, Bar: Explorer } | ||
| interfaces.Foo.matches('interface Foo { x: number; }'); // true | ||
| interfaces.Bar.matches('interface Bar { y: string; }'); // true | ||
| ``` | ||
|
|
||
| #### `classes` | ||
|
|
||
| Returns an object mapping class names to `Explorer` instances for all classes in the current scope. | ||
|
|
||
| ```js | ||
| const explorer = await __helpers.Explorer( | ||
| 'class Foo { x: number; } class Bar { y: string; }' | ||
| ); | ||
| const { classes } = explorer; // { Foo: Explorer, Bar: Explorer } | ||
| classes.Foo.matches('class Foo { x: number; }'); // true | ||
| classes.Bar.matches('class Bar { y: string; }'); // true | ||
| ``` | ||
|
|
||
| #### `methods` | ||
|
|
||
| Returns an object mapping method names to `Explorer` instances for all methods in the current class. | ||
|
|
||
| ```js | ||
| const explorer = await __helpers.Explorer( | ||
| 'class Foo { method1() {} method2() {} }' | ||
| ); | ||
| const { Foo } = explorer.classes; | ||
| const { method1, method2 } = Foo.methods; // { method1: Explorer, method2: Explorer } | ||
| ``` | ||
|
|
||
| #### `classConstructor`, `classProps`, and `constructorProps` | ||
|
|
||
| Use these to inspect class constructors and class-related properties. | ||
|
|
||
| ```js | ||
| const explorer = await __helpers.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 } | ||
| ``` | ||
|
|
||
| #### `typeProps` | ||
|
|
||
| Returns an object mapping property names to `Explorer` instances for all properties in a type, interface, or type literal. | ||
|
|
||
| ```js | ||
| const explorer = await __helpers.Explorer( | ||
| 'type Foo = { x: number; y: string; };' | ||
| ); | ||
| const { Foo } = explorer.types; | ||
| const { x, y } = Foo.typeProps; // { 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 = await __helpers.Explorer( | ||
| 'type Foo = { x: number; y: string; z?: boolean; };' | ||
| ); | ||
| const { Foo } = explorer.types; | ||
| 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 whose members exactly match the specified types, ignoring order. | ||
|
|
||
| ```js | ||
| 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 | ||
| ``` | ||
|
Comment on lines
+1514
to
+1525
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's not completely obvious that this is the expected behaviour, so I would update the wording. |
||
|
|
||
| #### `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 = 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 | ||
| a.value.hasCast('string'); // false | ||
| b.value.hasCast('string'); // true | ||
| ``` | ||
|
|
||
| #### `hasNonNullAssertion()` | ||
|
|
||
| Checks whether the current expression is a non-null assertion (`!`). | ||
|
|
||
| ```js | ||
| const explorer = await __helpers.Explorer('const a = maybeValue!;'); | ||
| const { a } = explorer.variables; | ||
| a.value.hasNonNullAssertion(); // 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('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('Spam'); // 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 = await __helpers.Explorer('class Foo implements Bar, Baz { }'); | ||
| 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 `<T>`), and `typeArguments` returns generic usages (like `<string>`). | ||
|
|
||
| ```js | ||
| const explorer = await __helpers.Explorer( | ||
| 'function id<T>(x: T): T { return x; }' | ||
| ); | ||
| const { id } = explorer.functions; | ||
| id.typeParameters[0].matches('T'); // true | ||
|
|
||
| const explorer2 = await __helpers.Explorer( | ||
| 'const m: Map<string, number> = 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 = await __helpers.Explorer( | ||
| 'class Foo { private readonly x: number; }' | ||
| ); | ||
| const { Foo } = explorer.classes; | ||
| const { x } = Foo.classProps; | ||
| x.isPrivate(); // true | ||
| x.isReadOnly(); // true | ||
| ``` | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.