-
-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Closes GH-9. Reviewed-by: Junyoung Choi <fluke8259@gmail.com> Reviewed-by: Titus Wormer <tituswormer@gmail.com> Co-authored-by: Junyoung Choi <fluke8259@gmail.com>
- Loading branch information
Showing
6 changed files
with
302 additions
and
3 deletions.
There are no files selected for viewing
This file contains 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 |
---|---|---|
@@ -0,0 +1,6 @@ | ||
import {Test, TestFunction} from './' | ||
import {Node} from 'unist' | ||
|
||
declare function convert<T extends Node>(test: Test<T>): TestFunction<T> | ||
|
||
export = convert |
This file contains 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 |
---|---|---|
@@ -0,0 +1,66 @@ | ||
// TypeScript Version: 3.5 | ||
|
||
import {Node, Parent} from 'unist' | ||
|
||
declare namespace unistUtilIs { | ||
/** | ||
* Check that type property matches expectation for a node | ||
* | ||
* @typeParam T type of node that passes test | ||
*/ | ||
type TestType<T extends Node> = T['type'] | ||
|
||
/** | ||
* Check that some attributes on a node are matched | ||
* | ||
* @typeParam T type of node that passes test | ||
*/ | ||
type TestObject<T extends Node> = Partial<T> | ||
|
||
/** | ||
* Check if a node passes a test | ||
* | ||
* @param node node to check | ||
* @param index index of node in parent | ||
* @param parent parent of node | ||
* @typeParam T type of node that passes test | ||
* @returns true if type T is found, false otherwise | ||
*/ | ||
type TestFunction<T extends Node> = ( | ||
node: unknown, | ||
index?: number, | ||
parent?: Parent | ||
) => node is T | ||
|
||
/** | ||
* Union of all the types of tests | ||
* | ||
* @typeParam T type of node that passes test | ||
*/ | ||
type Test<T extends Node> = TestType<T> | TestObject<T> | TestFunction<T> | ||
} | ||
|
||
/** | ||
* Unist utility to check if a node passes a test. | ||
* | ||
* @param node Node to check. | ||
* @param test When not given, checks if `node` is a `Node`. | ||
* When `string`, works like passing `function (node) {return node.type === test}`. | ||
* When `function` checks if function passed the node is true. | ||
* When `object`, checks that all keys in test are in node, and that they have (strictly) equal values. | ||
* When `array`, checks any one of the subtests pass. | ||
* @param index Position of `node` in `parent` | ||
* @param parent Parent of `node` | ||
* @param context Context object to invoke `test` with | ||
* @typeParam T type that node is compared with | ||
* @returns Whether test passed and `node` is a `Node` (object with `type` set to non-empty `string`). | ||
*/ | ||
declare function unistUtilIs<T extends Node>( | ||
node: unknown, | ||
test: unistUtilIs.Test<T> | Array<unistUtilIs.Test<any>>, | ||
index?: number, | ||
parent?: Parent, | ||
context?: any | ||
): node is T | ||
|
||
export = unistUtilIs |
This file contains 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
This file contains 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 |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"compilerOptions": { | ||
"lib": ["es2015"], | ||
"strict": true, | ||
"noImplicitAny": true, | ||
"noImplicitThis": true, | ||
"strictNullChecks": true, | ||
"strictFunctionTypes": true, | ||
"baseUrl": ".", | ||
"paths": { | ||
"unist-util-is": ["index.d.ts"], | ||
"unist-util-is/convert": ["convert.d.ts"] | ||
} | ||
} | ||
} |
This file contains 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 |
---|---|---|
@@ -0,0 +1,15 @@ | ||
{ | ||
"extends": "dtslint/dtslint.json", | ||
"rules": { | ||
"callable-types": false, | ||
"max-line-length": false, | ||
"no-redundant-jsdoc": false, | ||
"no-void-expression": false, | ||
"only-arrow-functions": false, | ||
"semicolon": false, | ||
"unified-signatures": false, | ||
"whitespace": false, | ||
"interface-over-type-literal": false, | ||
"no-unnecessary-generics": false | ||
} | ||
} |
This file contains 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 |
---|---|---|
@@ -0,0 +1,190 @@ | ||
import {Node, Parent} from 'unist' | ||
import unified = require('unified') | ||
import is = require('unist-util-is') | ||
import convert = require('unist-util-is/convert') | ||
|
||
/*=== setup ===*/ | ||
interface Heading extends Parent { | ||
type: 'heading' | ||
depth: number | ||
children: Node[] | ||
} | ||
|
||
interface Element extends Parent { | ||
type: 'element' | ||
tagName: string | ||
properties: { | ||
[key: string]: unknown | ||
} | ||
content: Node | ||
children: Node[] | ||
} | ||
|
||
interface Paragraph extends Parent { | ||
type: 'ParagraphNode' | ||
} | ||
|
||
const heading: Node = { | ||
type: 'heading', | ||
depth: 2, | ||
children: [] | ||
} | ||
|
||
const element: Node = { | ||
type: 'element', | ||
tagName: 'section', | ||
properties: {}, | ||
content: {type: 'text'}, | ||
children: [] | ||
} | ||
|
||
const isHeading = (node: unknown): node is Heading => | ||
typeof node === 'object' && node !== null && (node as Node).type === 'heading' | ||
const isElement = (node: unknown): node is Element => | ||
typeof node === 'object' && node !== null && (node as Node).type === 'element' | ||
|
||
/*=== types cannot be narrowed without predicate ===*/ | ||
// $ExpectError | ||
const maybeHeading: Heading = heading | ||
// $ExpectError | ||
const maybeElement: Element = element | ||
|
||
/*=== missing params ===*/ | ||
// $ExpectError | ||
is() | ||
// $ExpectError | ||
is<Node>() | ||
// $ExpectError | ||
is<Node>(heading) | ||
|
||
/*=== invalid generic ===*/ | ||
// $ExpectError | ||
is<string>(heading, 'heading') | ||
// $ExpectError | ||
is<boolean>(heading, 'heading') | ||
// $ExpectError | ||
is<{}>(heading, 'heading') | ||
|
||
/*=== assignable to boolean ===*/ | ||
const wasItAHeading: boolean = is<Heading>(heading, 'heading') | ||
|
||
/*=== type string test ===*/ | ||
is<Heading>(heading, 'heading') | ||
is<Heading>(element, 'heading') | ||
// $ExpectError | ||
is<Heading>(heading, 'element') | ||
|
||
if (is<Heading>(heading, 'heading')) { | ||
const maybeHeading: Heading = heading | ||
// $ExpectError | ||
const maybeNotHeading: Element = heading | ||
} | ||
|
||
is<Element>(element, 'element') | ||
is<Element>(heading, 'element') | ||
// $ExpectError | ||
is<Element>(element, 'heading') | ||
|
||
if (is<Element>(element, 'element')) { | ||
const maybeElement: Element = element | ||
// $ExpectError | ||
const maybeNotElement: Heading = element | ||
} | ||
|
||
/*=== type predicate function test ===*/ | ||
is(heading, isHeading) | ||
is(element, isHeading) | ||
// $ExpectError | ||
is<Heading>(heading, isElement) | ||
|
||
if (is(heading, isHeading)) { | ||
const maybeHeading: Heading = heading | ||
// $ExpectError | ||
const maybeNotHeading: Element = heading | ||
} | ||
|
||
is(element, isElement) | ||
is(heading, isElement) | ||
// $ExpectError | ||
is<Element>(element, isHeading) | ||
|
||
if (is(element, isElement)) { | ||
const maybeElement: Element = element | ||
// $ExpectError | ||
const maybeNotElement: Heading = element | ||
} | ||
|
||
/*=== type object test ===*/ | ||
is<Heading>(heading, {type: 'heading', depth: 2}) | ||
is<Heading>(element, {type: 'heading', depth: 2}) | ||
// $ExpectError | ||
is<Heading>(heading, {type: 'heading', depth: '2'}) | ||
|
||
if (is<Heading>(heading, {type: 'heading', depth: 2})) { | ||
const maybeHeading: Heading = heading | ||
// $ExpectError | ||
const maybeNotHeading: Element = heading | ||
} | ||
|
||
is<Element>(element, {type: 'element', tagName: 'section'}) | ||
is<Element>(heading, {type: 'element', tagName: 'section'}) | ||
// $ExpectError | ||
is<Element>(element, {type: 'element', tagName: true}) | ||
|
||
if (is<Element>(element, {type: 'element', tagName: 'section'})) { | ||
const maybeElement: Element = element | ||
// $ExpectError | ||
const maybeNotElement: Heading = element | ||
} | ||
|
||
/*=== type array of tests ===*/ | ||
is<Heading | Element | Paragraph>(heading, [ | ||
'heading', | ||
isElement, | ||
{type: 'ParagraphNode'} | ||
]) | ||
if ( | ||
is<Heading | Element | Paragraph>(heading, [ | ||
'heading', | ||
isElement, | ||
{type: 'ParagraphNode'} | ||
]) | ||
) { | ||
switch (heading.type) { | ||
case 'heading': { | ||
heading // $ExpectType Heading | ||
break | ||
} | ||
case 'element': { | ||
heading // $ExpectType Element | ||
break | ||
} | ||
case 'ParagraphNode': { | ||
heading // $ExpectType Paragraph | ||
break | ||
} | ||
// $ExpectError | ||
case 'dne': { | ||
break | ||
} | ||
} | ||
} | ||
|
||
/*=== usable in unified transform ===*/ | ||
unified().use(() => tree => { | ||
if (is<Heading>(tree, 'heading')) { | ||
// do something | ||
} | ||
return tree | ||
}) | ||
|
||
/*=== convert ===*/ | ||
convert<Heading>('heading') | ||
// $ExpectError | ||
convert<Heading>('element') | ||
convert<Heading>({type: 'heading', depth: 2}) | ||
// $ExpectError | ||
convert<Element>({type: 'heading', depth: 2}) | ||
convert<Heading>(isHeading) | ||
// $ExpectError | ||
convert<Element>(isHeading) |