Skip to content

Commit 56e0d3b

Browse files
authored
docs: add TS helpers (#1236)
1 parent 4e3a5b0 commit 56e0d3b

1 file changed

Lines changed: 359 additions & 0 deletions

File tree

src/content/docs/curriculum-help.mdx

Lines changed: 359 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1722,3 +1722,362 @@ def add(
17221722
):
17231723
return x + y
17241724
```
1725+
1726+
## TypeScript Helpers
1727+
1728+
These helpers provide Abstract Syntax Tree (AST) analysis capabilities for TypeScript challenges.
1729+
1730+
### Basic Usage
1731+
1732+
`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:
1733+
1734+
```js
1735+
const explorer = await __helpers.Explorer(code);
1736+
```
1737+
1738+
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:
1739+
1740+
```ts
1741+
class Spam {
1742+
get42(): number {
1743+
return 42;
1744+
}
1745+
}
1746+
```
1747+
1748+
To check the return type annotation you would write:
1749+
1750+
```js
1751+
const explorer = await __helpers.Explorer(code);
1752+
assert.isTrue(
1753+
explorer.classes.Spam.methods.get42.hasReturnAnnotation('number')
1754+
);
1755+
```
1756+
1757+
### Methods and Properties
1758+
1759+
#### `isEmpty()`
1760+
1761+
Returns `true` if the Explorer instance has no AST node, otherwise returns `false`.
1762+
1763+
```js
1764+
const explorer = await __helpers.Explorer('const a = 1;');
1765+
explorer.isEmpty(); // false
1766+
```
1767+
1768+
#### `toString()`
1769+
1770+
Returns the source code representation of the current node, or `"no ast"` if the node is empty.
1771+
1772+
```js
1773+
const explorer = await __helpers.Explorer('const a = 1;');
1774+
explorer.toString(); // "const a = 1;"
1775+
```
1776+
1777+
#### `matches(other: string | Explorer)`
1778+
1779+
Compares the current tree with another tree or string, ignoring semicolons and irrelevant whitespace differences.
1780+
1781+
```js
1782+
const explorer = await __helpers.Explorer('const a = 1;');
1783+
explorer.matches('const a = 1'); // true (ignores whitespace and semicolons)
1784+
explorer.matches('const b = 1;'); // false
1785+
```
1786+
1787+
#### `variables`
1788+
1789+
Returns an object mapping variable names to `Explorer` instances for all variables in the current scope.
1790+
1791+
```js
1792+
const explorer = await __helpers.Explorer(
1793+
'var a = 1; let b = 2; const c = () => {}'
1794+
);
1795+
const { a, b, c } = explorer.variables; // { a: Explorer, b: Explorer, c: Explorer }
1796+
a.matches('var a = 1;'); // true
1797+
b.matches('let b = 2;'); // true
1798+
c.matches('const c = () => {}'); // true
1799+
```
1800+
1801+
#### `value`
1802+
1803+
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.
1804+
1805+
```js
1806+
const explorer = await __helpers.Explorer('const a = 1; const b = { x: 10 };');
1807+
const { a, b } = explorer.variables;
1808+
a.value.toString(); // "1"
1809+
b.value.matches('{ x: 10 }'); // true
1810+
```
1811+
1812+
#### `objectProps`
1813+
1814+
Returns an object mapping property names to `Explorer` instances for all properties in an object literal.
1815+
1816+
```js
1817+
const explorer = await __helpers.Explorer(
1818+
"const obj = { x: 1, y: 'hello', z: true };"
1819+
);
1820+
const { obj } = explorer.variables;
1821+
const props = obj.value.objectProps; // { x: Explorer, y: Explorer, z: Explorer }
1822+
```
1823+
1824+
#### `functions` and `allFunctions`
1825+
1826+
`functions` returns function declarations only. `allFunctions` also includes arrow functions and function expressions assigned to variables.
1827+
1828+
```js
1829+
const explorer = await __helpers.Explorer(
1830+
'function foo() { return 42; } const bar = () => 24;'
1831+
);
1832+
explorer.functions; // { foo: Explorer }
1833+
explorer.allFunctions; // { foo: Explorer, bar: Explorer }
1834+
```
1835+
1836+
#### `parameters`
1837+
1838+
Returns an array of `Explorer` instances representing the parameters of a function or method.
1839+
1840+
```js
1841+
const explorer = await __helpers.Explorer(
1842+
'function foo(x: number, y: string) { return 42; }'
1843+
);
1844+
const parameters = explorer.functions.foo.parameters;
1845+
parameters[0].toString(); // x: number
1846+
parameters[1].toString(); // y: string
1847+
```
1848+
1849+
#### `annotation`
1850+
1851+
Returns an `Explorer` instance representing the type annotation of the current node, or an empty `Explorer` if none exists.
1852+
1853+
```js
1854+
const explorer = await __helpers.Explorer(
1855+
'const a: number = 1; const b: { age: number } = { age: 33 }'
1856+
);
1857+
const { a, b } = explorer.variables;
1858+
a.annotation; // Explorer with "number"
1859+
b.annotation; // Explorer with "{ age: number }"
1860+
```
1861+
1862+
#### `hasAnnotation(annotation: string)`
1863+
1864+
Returns `true` if the current node has a type annotation matching the specified string.
1865+
1866+
```js
1867+
const explorer = await __helpers.Explorer('const a: number = 1;');
1868+
const { a } = explorer.variables;
1869+
a.hasAnnotation('number'); // true
1870+
a.hasAnnotation('string'); // false
1871+
```
1872+
1873+
#### `hasReturnAnnotation(annotation: string)`
1874+
1875+
Returns `true` if the function/method has a return type annotation matching the specified type.
1876+
1877+
```js
1878+
const explorer = await __helpers.Explorer(
1879+
'function foo(): number { return 42; }'
1880+
);
1881+
const { foo } = explorer.functions;
1882+
foo.hasReturnAnnotation('number'); // true
1883+
foo.hasReturnAnnotation('string'); // false
1884+
```
1885+
1886+
#### `hasReturn(value: string)`
1887+
1888+
Checks whether a function, method, or function-valued variable has a matching top-level return expression.
1889+
1890+
```js
1891+
const explorer = await __helpers.Explorer('function foo() { return 42; }');
1892+
const { foo } = explorer.functions;
1893+
foo.hasReturn('42'); // true
1894+
```
1895+
1896+
#### `types`
1897+
1898+
Returns an object mapping type alias names to `Explorer` instances for all type aliases in the current scope.
1899+
1900+
```js
1901+
const explorer = await __helpers.Explorer(
1902+
'type Foo = { x: number; }; type Bar = { y: string; };'
1903+
);
1904+
const { types } = explorer; // { Foo: Explorer, Bar: Explorer }
1905+
types.Foo.matches('type Foo = { x: number; };'); // true
1906+
types.Bar.matches('type Bar = { y: string; };'); // true
1907+
```
1908+
1909+
#### `interfaces`
1910+
1911+
Returns an object mapping interface names to `Explorer` instances for all interfaces in the current scope.
1912+
1913+
```js
1914+
const explorer = await __helpers.Explorer(
1915+
'interface Foo { x: number; } interface Bar { y: string; }'
1916+
);
1917+
const { interfaces } = explorer; // { Foo: Explorer, Bar: Explorer }
1918+
interfaces.Foo.matches('interface Foo { x: number; }'); // true
1919+
interfaces.Bar.matches('interface Bar { y: string; }'); // true
1920+
```
1921+
1922+
#### `classes`
1923+
1924+
Returns an object mapping class names to `Explorer` instances for all classes in the current scope.
1925+
1926+
```js
1927+
const explorer = await __helpers.Explorer(
1928+
'class Foo { x: number; } class Bar { y: string; }'
1929+
);
1930+
const { classes } = explorer; // { Foo: Explorer, Bar: Explorer }
1931+
classes.Foo.matches('class Foo { x: number; }'); // true
1932+
classes.Bar.matches('class Bar { y: string; }'); // true
1933+
```
1934+
1935+
#### `methods`
1936+
1937+
Returns an object mapping method names to `Explorer` instances for all methods in the current class.
1938+
1939+
```js
1940+
const explorer = await __helpers.Explorer(
1941+
'class Foo { method1() {} method2() {} }'
1942+
);
1943+
const { Foo } = explorer.classes;
1944+
const { method1, method2 } = Foo.methods; // { method1: Explorer, method2: Explorer }
1945+
```
1946+
1947+
#### `classConstructor`, `classProps`, and `constructorProps`
1948+
1949+
Use these to inspect class constructors and class-related properties.
1950+
1951+
```js
1952+
const explorer = await __helpers.Explorer(
1953+
'class Foo { prop1: number; constructor(x) { this.y = x; } }'
1954+
);
1955+
const { Foo } = explorer.classes;
1956+
Foo.classConstructor?.matches('constructor(x) { this.y = x; }'); // true
1957+
Foo.classProps; // { prop1: Explorer }
1958+
Foo.constructorProps; // { y: Explorer }
1959+
```
1960+
1961+
#### `typeProps`
1962+
1963+
Returns an object mapping property names to `Explorer` instances for all properties in a type, interface, or type literal.
1964+
1965+
```js
1966+
const explorer = await __helpers.Explorer(
1967+
'type Foo = { x: number; y: string; };'
1968+
);
1969+
const { Foo } = explorer.types;
1970+
const { x, y } = Foo.typeProps; // { x: Explorer, y: Explorer }
1971+
```
1972+
1973+
#### `hasTypeProps(props: TypeProp | TypeProp[])`
1974+
1975+
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 }`.
1976+
1977+
```js
1978+
const explorer = await __helpers.Explorer(
1979+
'type Foo = { x: number; y: string; z?: boolean; };'
1980+
);
1981+
const { Foo } = explorer.types;
1982+
Foo.hasTypeProps({ name: 'x' }); // true
1983+
Foo.hasTypeProps([
1984+
{ name: 'x', type: 'number' },
1985+
{ name: 'y', type: 'string', isOptional: false },
1986+
{ name: 'z', isOptional: true }
1987+
]); // true
1988+
Foo.hasTypeProps({ name: 'a' }); // false
1989+
```
1990+
1991+
#### `isUnionOf(types: string[])`
1992+
1993+
Returns `true` if the current node has a union type annotation whose members exactly match the specified types, ignoring order.
1994+
1995+
```js
1996+
const explorer = await __helpers.Explorer(
1997+
'const a: number | string | boolean;'
1998+
);
1999+
const { a } = explorer.variables;
2000+
a.annotation.isUnionOf(['number', 'string', 'boolean']); // true
2001+
a.annotation.isUnionOf(['number', 'string']); // false
2002+
```
2003+
2004+
#### `hasCast(expectedType?: string)`
2005+
2006+
Checks if the current node is a type assertion (cast using `as`). Optionally verify the cast type matches the provided type string.
2007+
2008+
```js
2009+
const explorer = await __helpers.Explorer(
2010+
'const a = 1 as number; const b = 2 as string;'
2011+
);
2012+
const { a, b } = explorer.variables;
2013+
a.value.hasCast(); // true
2014+
a.value.hasCast('number'); // true
2015+
a.value.hasCast('string'); // false
2016+
b.value.hasCast('string'); // true
2017+
```
2018+
2019+
#### `hasNonNullAssertion()`
2020+
2021+
Checks whether the current expression is a non-null assertion (`!`).
2022+
2023+
```js
2024+
const explorer = await __helpers.Explorer('const a = maybeValue!;');
2025+
const { a } = explorer.variables;
2026+
a.value.hasNonNullAssertion(); // true
2027+
```
2028+
2029+
#### `doesExtend(basesToCheck: string | string[])`
2030+
2031+
Checks if a class or interface extends the specified base class or interface(s). Accepts a single string or an array of strings.
2032+
2033+
```js
2034+
const explorer = new Explorer('interface Foo extends Bar, Baz { }');
2035+
const { Foo } = explorer.interfaces;
2036+
Foo.doesExtend('Bar'); // true
2037+
Foo.doesExtend(['Bar', 'Baz']); // true only if both are extended
2038+
Foo.doesExtend('Spam'); // false
2039+
```
2040+
2041+
#### `doesImplement(basesToCheck: string | string[])`
2042+
2043+
Checks if a class implements the specified interface(s). Accepts a single string or an array of strings.
2044+
2045+
```js
2046+
const explorer = await __helpers.Explorer('class Foo implements Bar, Baz { }');
2047+
const { Foo } = explorer.classes;
2048+
Foo.doesImplement('Bar'); // true
2049+
Foo.doesImplement(['Bar', 'Baz']); // true
2050+
Foo.doesImplement('Spam'); // false
2051+
```
2052+
2053+
#### `typeParameters` and `typeArguments`
2054+
2055+
`typeParameters` returns generic declarations (like `<T>`), and `typeArguments` returns generic usages (like `<string>`).
2056+
2057+
```js
2058+
const explorer = await __helpers.Explorer(
2059+
'function id<T>(x: T): T { return x; }'
2060+
);
2061+
const { id } = explorer.functions;
2062+
id.typeParameters[0].matches('T'); // true
2063+
2064+
const explorer2 = await __helpers.Explorer(
2065+
'const m: Map<string, number> = new Map();'
2066+
);
2067+
const { m } = explorer2.variables;
2068+
m.annotation.typeArguments[0].matches('string'); // true
2069+
```
2070+
2071+
#### Access modifiers
2072+
2073+
Use `isPrivate()`, `isProtected()`, `isPublic()`, and `isReadOnly()` on class members and type properties.
2074+
2075+
```js
2076+
const explorer = await __helpers.Explorer(
2077+
'class Foo { private readonly x: number; }'
2078+
);
2079+
const { Foo } = explorer.classes;
2080+
const { x } = Foo.classProps;
2081+
x.isPrivate(); // true
2082+
x.isReadOnly(); // true
2083+
```

0 commit comments

Comments
 (0)