11/* eslint-disable @typescript-eslint/no-confusing-void-expression, @typescript-eslint/no-empty-function */
22
3- import { expectError } from 'tsd'
4- import { Node , Parent } from 'unist'
3+ import { expectError , expectType } from 'tsd'
4+ import { Node , Parent , Literal } from 'unist'
5+ import { is } from 'unist-util-is'
56import { visit , SKIP , EXIT , CONTINUE } from './index.js'
67
78/* Setup */
8- const sampleTree = {
9+ const sampleTree : Root = {
910 type : 'root' ,
1011 children : [ { type : 'heading' , depth : 1 , children : [ ] } ]
1112}
1213
13- interface Heading extends Parent {
14- type : 'heading'
15- depth : number
16- children : Node [ ]
14+ const complexTree : Root = {
15+ type : 'root' ,
16+ children : [
17+ {
18+ type : 'blockquote' ,
19+ children : [ { type : 'paragraph' , children : [ { type : 'text' , value : 'a' } ] } ]
20+ } ,
21+ {
22+ type : 'paragraph' ,
23+ children : [
24+ {
25+ type : 'emphasis' ,
26+ children : [ { type : 'emphasis' , children : [ { type : 'text' , value : 'b' } ] } ]
27+ } ,
28+ { type : 'text' , value : 'c' }
29+ ]
30+ }
31+ ]
1732}
1833
1934interface Element extends Parent {
@@ -24,6 +39,43 @@ interface Element extends Parent {
2439 children : Node [ ]
2540}
2641
42+ type Content = Flow | Phrasing
43+
44+ interface Root extends Parent {
45+ type : 'root'
46+ children : Flow [ ]
47+ }
48+
49+ type Flow = Blockquote | Heading | Paragraph
50+
51+ interface Blockquote extends Parent {
52+ type : 'blockquote'
53+ children : Flow [ ]
54+ }
55+
56+ interface Heading extends Parent {
57+ type : 'heading'
58+ depth : number
59+ children : Phrasing [ ]
60+ }
61+
62+ interface Paragraph extends Parent {
63+ type : 'paragraph'
64+ children : Phrasing [ ]
65+ }
66+
67+ type Phrasing = Text | Emphasis
68+
69+ interface Emphasis extends Parent {
70+ type : 'emphasis'
71+ children : Phrasing [ ]
72+ }
73+
74+ interface Text extends Literal {
75+ type : 'text'
76+ value : string
77+ }
78+
2779const isNode = ( node : unknown ) : node is Node =>
2880 typeof node === 'object' && node !== null && 'type' in node
2981const headingTest = ( node : unknown ) : node is Heading =>
@@ -36,68 +88,98 @@ expectError(visit())
3688expectError ( visit ( sampleTree ) )
3789
3890/* Visit without test. */
39- visit ( sampleTree , ( _ ) => { } )
40- visit ( sampleTree , ( _ : Node ) => { } )
41- expectError ( visit ( sampleTree , ( _ : Element ) => { } ) )
42- expectError ( visit ( sampleTree , ( _ : Heading ) => { } ) )
91+ visit ( sampleTree , ( node ) => {
92+ expectType < Root | Content > ( node )
93+ } )
4394
4495/* Visit with type test. */
45- visit ( sampleTree , 'heading' , ( _ ) => { } )
46- visit ( sampleTree , 'heading' , ( _ : Heading ) => { } )
47- expectError ( visit ( sampleTree , 'not-a-heading' , ( _ : Heading ) => { } ) )
48- expectError ( visit ( sampleTree , 'element' , ( _ : Heading ) => { } ) )
49-
50- visit ( sampleTree , 'element' , ( _ ) => { } )
51- visit ( sampleTree , 'element' , ( _ : Element ) => { } )
52- expectError ( visit ( sampleTree , 'not-an-element' , ( _ : Element ) => { } ) )
96+ visit ( sampleTree , 'heading' , ( node ) => {
97+ expectType < Heading > ( node )
98+ } )
99+ visit ( sampleTree , 'element' , ( node ) => {
100+ // Not in tree.
101+ expectType < never > ( node )
102+ } )
53103expectError ( visit ( sampleTree , 'heading' , ( _ : Element ) => { } ) )
54104
55105/* Visit with object test. */
56- visit ( sampleTree , { type : 'heading' } , ( _ ) => { } )
57- visit ( sampleTree , { random : 'property' } , ( _ ) => { } )
58-
59- visit ( sampleTree , { type : 'heading ' } , ( _ : Heading ) => { } )
60- visit ( sampleTree , { type : 'heading' , depth : 2 } , ( _ : Heading ) => { } )
61- expectError ( visit ( sampleTree , { type : 'element' } , ( _ : Heading ) => { } ) )
62- expectError (
63- visit ( sampleTree , { type : 'heading' , depth : '2' } , ( _ : Heading ) => { } )
64- )
65-
66- visit ( sampleTree , { type : 'element ' } , ( _ : Element ) => { } )
67- visit ( sampleTree , { type : 'element' , tagName : 'section' } , ( _ : Element ) => { } )
68-
69- expectError ( visit ( sampleTree , { type : 'heading' } , ( _ : Element ) => { } ) )
70-
71- expectError (
72- visit ( sampleTree , { type : 'element' , tagName : true } , ( _ : Element ) => { } )
73- )
106+ visit ( sampleTree , { depth : 1 } , ( node ) => {
107+ expectType < Heading > ( node )
108+ } )
109+ visit ( sampleTree , { random : 'property ' } , ( node ) => {
110+ expectType < never > ( node )
111+ } )
112+ visit ( sampleTree , { type : 'heading' , depth : '2' } , ( node ) => {
113+ // Not in tree.
114+ expectType < never > ( node )
115+ } )
116+ visit ( sampleTree , { tagName : 'section ' } , ( node ) => {
117+ // Not in tree.
118+ expectType < never > ( node )
119+ } )
120+ visit ( sampleTree , { type : 'element' , tagName : 'section' } , ( node ) => {
121+ // Not in tree.
122+ expectType < never > ( node )
123+ } )
74124
75125/* Visit with function test. */
76- visit ( sampleTree , headingTest , ( _ ) => { } )
77- visit ( sampleTree , headingTest , ( _ : Heading ) => { } )
126+ visit ( sampleTree , headingTest , ( node ) => {
127+ expectType < Heading > ( node )
128+ } )
78129expectError ( visit ( sampleTree , headingTest , ( _ : Element ) => { } ) )
79-
80- visit ( sampleTree , elementTest , ( _ ) => { } )
81- visit ( sampleTree , elementTest , ( _ : Element ) => { } )
82- expectError ( visit ( sampleTree , elementTest , ( _ : Heading ) => { } ) )
130+ visit ( sampleTree , elementTest , ( node ) => {
131+ // Not in tree.
132+ expectType < never > ( node )
133+ } )
83134
84135/* Visit with array of tests. */
85- visit ( sampleTree , [ 'ParagraphNode' , { type : 'element' } , headingTest ] , ( _ ) => { } )
136+ visit ( sampleTree , [ 'heading' , { depth : 1 } , headingTest ] , ( node ) => {
137+ // Unfortunately TS casts things in arrays too vague.
138+ expectType < Root | Content > ( node )
139+ } )
86140
87141/* Visit returns action. */
88- visit ( sampleTree , 'heading' , ( _ ) => CONTINUE )
89- visit ( sampleTree , 'heading' , ( _ ) => EXIT )
90- visit ( sampleTree , 'heading' , ( _ ) => SKIP )
91- expectError ( visit ( sampleTree , 'heading' , ( _ ) => 'random' ) )
142+ visit ( sampleTree , ( ) => CONTINUE )
143+ visit ( sampleTree , ( ) => EXIT )
144+ visit ( sampleTree , ( ) => SKIP )
145+ expectError ( visit ( sampleTree , ( ) => 'random' ) )
92146
93147/* Visit returns index. */
94- visit ( sampleTree , 'heading' , ( _ ) => 0 )
95- visit ( sampleTree , 'heading' , ( _ ) => 1 )
148+ visit ( sampleTree , ( ) => 0 )
149+ visit ( sampleTree , ( ) => 1 )
96150
97151/* Visit returns tuple. */
98- visit ( sampleTree , 'heading' , ( _ ) => [ CONTINUE , 1 ] )
99- visit ( sampleTree , 'heading' , ( _ ) => [ EXIT , 1 ] )
100- visit ( sampleTree , 'heading' , ( _ ) => [ SKIP , 1 ] )
101- visit ( sampleTree , 'heading' , ( _ ) => [ SKIP ] )
102- expectError ( visit ( sampleTree , 'heading' , ( _ ) => [ 1 ] ) )
103- expectError ( visit ( sampleTree , 'heading' , ( _ ) => [ 'random' , 1 ] ) )
152+ visit ( sampleTree , ( ) => [ CONTINUE , 1 ] )
153+ visit ( sampleTree , ( ) => [ EXIT , 1 ] )
154+ visit ( sampleTree , ( ) => [ SKIP , 1 ] )
155+ visit ( sampleTree , ( ) => [ SKIP ] )
156+ expectError ( visit ( sampleTree , ( ) => [ 1 ] ) )
157+ expectError ( visit ( sampleTree , ( ) => [ 'random' , 1 ] ) )
158+
159+ /* Should infer children from the given tree. */
160+ visit ( complexTree , ( node ) => {
161+ expectType < Root | Content > ( node )
162+ } )
163+
164+ const blockquote = complexTree . children [ 0 ]
165+ if ( is < Blockquote > ( blockquote , 'blockquote' ) ) {
166+ visit ( blockquote , ( node ) => {
167+ expectType < Content > ( node )
168+ } )
169+ }
170+
171+ const paragraph = complexTree . children [ 1 ]
172+ if ( is < Paragraph > ( paragraph , 'paragraph' ) ) {
173+ visit ( paragraph , ( node ) => {
174+ expectType < Paragraph | Phrasing > ( node )
175+ } )
176+
177+ const child = paragraph . children [ 1 ]
178+
179+ if ( is < Emphasis > ( child , 'emphasis' ) ) {
180+ visit ( child , 'blockquote' , ( node ) => {
181+ // `blockquote` does not exist in phrasing.
182+ expectType < never > ( node )
183+ } )
184+ }
185+ }
0 commit comments