You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The key feature of the new API is to automatically refine the node to a specific kind when the user gives more type information.
315
+
A standout feature of our new API is automatic type refinement based on contextual information. This happens seamlessly through the `field` method.
316
316
317
-
This is done by using the `field` method
317
+
When you access a node's field using `field("name")`, the system automatically examines the static type information and refines the node type accordingly:
318
318
319
-
`sgNode.field("kind")` will automatically check the field name and its corresponding types in the static type, and refine the node to the specific kind.
320
319
```typescript
321
320
let exportStmt:SgNode<'export_statement'>
322
-
exportStmt.field('declaration') // refine to SgNde<'function_declaration'> | SgNode<'variable_declaration'> ...
321
+
exportStmt.field('declaration') // Automatically refines to union:
322
+
// SgNode<'function_declaration'> |
323
+
// SgNode<'variable_declaration'> | ...
323
324
```
324
325
325
-
You don't need to explicitly spell out the kind! It is both **concise** and **correct**.
326
+
The magic here is that you never need to specify the possible types explicitly - the system infers them automatically. This approach is both **concise**in usage and **correct** in type inference.
326
327
328
+
### Exhaustive Pattern Matching with kindToRefine
327
329
328
-
### Exhaustive Check via `sgNode.kindToRefine`
330
+
We've also introduced a new `kindToRefine` property for comprehensive type checking. You might wonder: why add this when we already have a `kind()` method?
329
331
330
-
ast-grep/napi also introduced a new property `kindToRefine` to refine the node to a specific kind.
332
+
There are two key reasons:
333
+
1. Preserving backward compatibility with the existing `kind()` method
334
+
2. Enabling TypeScript's type narrowing, which works with properties but not method calls
331
335
332
-
Why do we need the `kindToRefine`property given that we already have a `kind()` method?
336
+
While `kindToRefine`is implemented as a getter that calls into Rust code (making it as computationally expensive as the `kind()` method), it enables powerful type checking capabilities. To ensure developers are aware of this **performance** characteristic, we deliberately chose a _distinct and longer_ property name.
333
337
334
-
First, `kind` is a method in the existing API and we prefer not to have a breaking change.
335
-
336
-
Secondly, TypeScript cannot narrow type via a method call. It can only narrow type via a property access.
337
-
338
-
In terms of implementation, `kindToRefine` is a getter under the hood powered by napi. It is less efficient thant JavaScript's object property access.
339
-
Actually, it will call Rust function from JavaScript, which is as expensive as the `kind()` method.
340
-
341
-
To bring user's awareness to this **performance** implication and to make a backward compatible API change, we introduce the `kindToRefine` property.
342
-
343
-
It is mostly useful for a union type of nodes with specific kinds, guiding you to write a **correct** AST program. You can use it in tandem with the union type returned by `RefinedNode` to exhaustively check all possible kinds of nodes.
338
+
This property really shines when working in tandem union types returned by `RefineNode`, helping you write **correct** AST transformations through exhaustive pattern matching:
func.kindToRefine//narrow to 'function_declaration'
345
+
func.kindToRefine//Narrowed to function_declaration
351
346
break
352
347
case'arrow_function':
353
-
func.kindToRefine//narrow to 'arrow_function'
348
+
func.kindToRefine//Narrowed to arrow_function
354
349
break
355
-
// ....
356
350
default:
357
-
funcsatisfiesnever//exhaustiveness, checked!
351
+
funcsatisfiesnever//TypeScript ensures we handled all cases
358
352
}
359
353
```
360
354
355
+
The combination of automatic type refinement and exhaustive pattern matching makes it easier to write **correct** AST transformations while catching potential errors at compile time.
356
+
361
357
## Confine Types
362
358
363
-
Be austere of type level programming. Too much type level programming can make the compiler explode, as well as users' brain.
359
+
Always bear in mind this mantra: _Be austere with type level programming._
360
+
361
+
Overdoing type level programming can overload the compiler as well as overwhelm users.
362
+
It is a good practice to confine the API type to a reasonable complexity level.
364
363
365
364
### Prune unnamed kinds
366
-
Tree-sitter's static type contains a lot of unnamed kinds, which are not useful to users.
367
365
368
-
For example `+`/`-`/`*`/`/` is too noisy for an AST library.
366
+
Tree-sitter's static type includes many unnamed kinds, which are not user-friendly.
367
+
368
+
For instance, operators like `+`/`-`/`*`/`/` are too verbose for an AST library. We're building a compiler plugin, not solving elementary school math problems, right?
369
369
370
-
This is also the reason why we need to include `string` in the `Kinds`.
370
+
This is why we exclude the unnamed kinds and include `string` in the `Kinds`.
371
371
372
372
In the type generation step, ast-grep filters out these unnamed kinds to make the type more **concise**.
373
373
374
374
### Opt-in refinement for better compile time performance
375
375
376
-
The new API is designed to provide a better type checking and completion experience to the user. But it comes with a cost of **performance**.
377
-
One type map for a single language can be several thousand lines of code with hundreds of kinds.
378
-
The more type information the user provides, the slower the compile time.
376
+
The new API is designed to provide a better type checking and autocompletion experience for users.
377
+
However, this improvement comes at the cost of **performance**. A single type map for one language can span several thousand lines of code with hundreds of kinds. The more type information the user provides, the slower the compile time.
379
378
379
+
To manage this, you need to explicitly opt into type information by passing type parameters to the `parse` method.
380
380
381
-
So you need to explicitly opt in type information by passing type parameters to `parse` method.
382
381
```typescript
383
382
import { parse } from'@ast-grep/napi'
384
-
importTSfrom'@ast-grep/napi/lang/TypeScript'
383
+
importTSfrom'@ast-grep/napi/lang/TypeScript'// import this can be slow
385
384
const untyped =parse(Lang.TypeScript, code)
386
385
const typed =parse<TS>(Lang.TypeScript, code)
387
386
```
388
387
389
388
### Typed Rule!
390
389
391
-
The last feature worth mentioning is the typed rule! You can even type the `kind` in rule JSON!
392
-
390
+
The last notable feature is the typed rule. You can even type the `kind` in rule JSON!
Of course this is not to _confine_the type, but let the type creep into the rule greatly improving the UX and rule **correctness**.
399
+
Of course, this isn't about _confining_the type but allowing type information to enhance rules, significantly improving UX and rule **correctness**.
402
400
403
401
You can look up the available kinds in the static type via the completion popup in your editor. (btw I use nvim)
402
+
404
403
```typescript
405
404
sgNode.find({
406
405
rule: {
@@ -412,11 +411,12 @@ sgNode.find({
412
411
413
412
## Ending
414
413
415
-
I'm very thrilled to see the future of AST manipulation in TypeScript.
416
-
This feature enables users to switch freely between untyped AST and typed AST.
414
+
I'm incredibly excited about the future of AST manipulation in TypeScript.
415
+
416
+
This feature empowers users to seamlessly switch between untyped and typed AST, offering flexibility and enhanced capabilities, an innovation that has not been seen in other AST libraries, especially not in native language based ones.
417
417
418
-
To use a quote from [Theo's video](https://www.youtube.com/clip/Ugkxn2oomDuyQjtaKXhYP1MU9TLEShf5m1nf):
418
+
As Theo aptly puts it in [his video](https://www.youtube.com/clip/Ugkxn2oomDuyQjtaKXhYP1MU9TLEShf5m1nf):
419
419
420
420
> There are very few devs that understands Rust deeply enough and compiler deeply enough that also care about TypeScript in web dev enough to build something for web devs in Rust
421
421
422
-
ast-grep will strive to be the one that bridges the gap between Rust and TypeScript.
422
+
ast-grep is determined to bridge that gap between Rust and TypeScript!
0 commit comments