Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions types/unist/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,16 @@
// Titus Wormer <https://github.com/wooorm>
// Junyoung Choi <https://github.com/rokt33r>
// Ben Moon <https://github.com/GuiltyDolphin>
// JounQin <https://github.com/JounQin>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 3.0

/**
* Syntactic units in unist syntax trees are called nodes.
*
* @typeParam TData Information from the ecosystem. Useful for more specific {@link Node.data}.
*/
export interface Node {
export interface Node<TData extends object = Data> {
/**
* The variant of a node.
*/
Expand All @@ -21,7 +24,7 @@ export interface Node {
/**
* Information from the ecosystem.
*/
data?: Data | undefined;
data?: TData | undefined;

/**
* Location of a node in a source document.
Expand Down Expand Up @@ -79,19 +82,33 @@ export interface Point {
offset?: number | undefined;
}

/**
* Util for extracting type of {@link Node.data}
*
* @typeParam TNode Specific node type such as {@link Node} with {@link Data}, {@link Literal}, etc.
*
* @example `NodeData<Node<{ key: string }>>` -> `{ key: string }`
*/
export type NodeData<TNode extends Node<object>> = TNode extends Node<infer TData> ? TData : never;

/**
* Nodes containing other nodes.
*
* @typeParam ChildNode Node item of {@link Parent.children}
*/
export interface Parent extends Node {
export interface Parent<ChildNode extends Node<object> = Node, TData extends object = NodeData<ChildNode>>
extends Node<TData> {
/**
* List representing the children of a node.
*/
children: Node[];
children: ChildNode[];
}

/**
* Nodes containing a value.
*
* @typeParam Value Specific value type of {@link Literal.value} such as `string` for `Text` node
*/
export interface Literal extends Node {
value: unknown;
export interface Literal<Value = unknown, TData extends object = Data> extends Node<TData> {
value: Value;
}
112 changes: 102 additions & 10 deletions types/unist/unist-tests.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,148 @@
import { Data, Point, Position, Node, Literal, Parent } from 'unist';
import { Data, Point, Position, Node, Literal, Parent, NodeData } from 'unist';

const data: Data = {
string: 'string',
number: 1,
object: {
key: 'value'
key: 'value',
},
array: [],
boolean: true,
null: null
null: null,
};

const point: Point = {
line: 1,
column: 1,
offset: 0
offset: 0,
};

const position: Position = {
start: point,
end: point,
indent: [1]
indent: [1],
};

const node: Node = {
type: 'node',
data,
position
position,
};

const text: Literal = {
type: 'text',
data,
position,
value: 'value'
value: 'value',
};

const parent: Parent = {
type: 'parent',
data,
position,
children: [node, text]
children: [node, text],
};

const noExtraKeysInNode: Node = {
type: 'noExtraKeysInNode',
// $ExpectError
extra: 'extra'
extra: 'extra',
};

const noChildrenInNode: Node = {
type: 'noChildrenInNode',
// $ExpectError
children: []
children: [],
};

const stringLiteral: Literal<string> = {
type: 'text',
data,
position,
value: 'value',
};

const literalParent: Parent<Literal<string>> = {
type: 'literalParent',
data,
position,
children: [stringLiteral],
};

const nodeData: Node<{ key: string }> = {
type: 'nodeData',
data: {
key: 'value',
},
};

const nodeData2: Node<{ key: string }> = {
type: 'nodeData',
// $ExpectError
data: {},
};

// $ExpectError
type DataType = NodeData<Node<string>>;

const literalData: Literal<string, { key: string }> = {
type: 'literalData',
data: {
key: 'value',
},
value: 'value',
};

const literalParentData: Parent<Literal<string>, Data> = {
type: 'literalParent',
data,
position,
children: [stringLiteral],
};

const data1 = {
key1: 'value1',
};

const data2 = {
key2: 'value2',
};

const nestedliteralParentData: Parent<Literal<string, typeof data1>, typeof data2> = {
type: 'literalParent',
data: data2,
position,
children: [
{
...stringLiteral,
data: data1,
},
],
};

function exampleNodeUtil(node: Node) {}

const inferredNode = { type: 'example' };
const inferredNotNode = { notType: 'whoops' };

exampleNodeUtil(inferredNode);
// $ExpectError
exampleNodeUtil(inferredNotNode);

function exampleLiteralUtil(node: Literal) {}

const inferredLiteral = { type: 'example', value: 'value' };
const inferredNotLiteral = { type: 'example' };

exampleLiteralUtil(inferredLiteral);
// $ExpectError
exampleLiteralUtil(inferredNotLiteral);

function exampleParentUtil(node: Parent) {}

const inferredParent = { type: 'example', children: [inferredNode] };
const inferredNotParent = { type: 'example', children1: [] };

exampleParentUtil(inferredParent);
// $ExpectError
exampleParentUtil(inferredNotParent);