use crate::parser::DocParser;
use crate::printer::DocPrinter;
use deno_graph::create_type_graph;
use deno_graph::source::MemoryLoader;
use deno_graph::source::Source;
use deno_graph::CapturingModuleAnalyzer;
use deno_graph::GraphOptions;
use deno_graph::ModuleGraph;
use deno_graph::ModuleSpecifier;
use pretty_assertions::assert_eq;
use serde_json::json;
type MaybeHeaders<S> = Option<Vec<(S, S)>>;
pub(crate) async fn setup<S: AsRef<str> + Copy>(
root: S,
sources: Vec<(S, MaybeHeaders<S>, S)>,
) -> (ModuleGraph, CapturingModuleAnalyzer, ModuleSpecifier) {
let sources = sources
.into_iter()
.map(|(s, h, c)| {
(
s,
Source::Module {
specifier: s,
maybe_headers: h,
content: c,
},
)
})
.collect();
let mut memory_loader = MemoryLoader::new(sources, vec![]);
let root = ModuleSpecifier::parse(root.as_ref()).unwrap();
let analyzer = CapturingModuleAnalyzer::default();
let graph = create_type_graph(
vec![root.clone()],
&mut memory_loader,
GraphOptions {
module_analyzer: Some(&analyzer),
..Default::default()
},
)
.await;
(graph, analyzer, root)
}
macro_rules! doc_test {
( $name:ident, $source:expr; $block:expr ) => {
doc_test!($name, $source, false; $block);
};
( $name:ident, $source:expr, private; $block:expr ) => {
doc_test!($name, $source, true; $block);
};
( $name:ident, $source:expr, $private:expr; $block:expr ) => {
#[tokio::test]
async fn $name() {
use super::setup;
let source_code = $source;
let private = $private;
let (graph, analyzer, specifier) = setup("file:///test.ts", vec![
("file:///test.ts", None, source_code)
]).await;
let entries = DocParser::new(graph, private, analyzer.as_capturing_parser())
.parse(&specifier)
.unwrap();
#[allow(unused_variables)]
let doc = DocPrinter::new(&entries, false, private).to_string();
#[allow(clippy::redundant_closure_call)]
($block)(entries, doc)
}
};
}
macro_rules! contains_test {
( $name:ident, $source:expr;
$( $contains:expr ),* $( ; $( $notcontains:expr ),* )? ) => {
contains_test!($name, $source, false; $($contains),* $(;$($notcontains),*)?);
};
( $name:ident, $source:expr, private;
$( $contains:expr ),* $( ; $( $notcontains:expr ),* )? ) => {
contains_test!($name, $source, true; $($contains),* $(;$($notcontains),*)?);
};
( $name:ident, $source:expr, $private:expr;
$( $contains:expr ),* $( ; $( $notcontains:expr ),* )? ) => {
doc_test!($name, $source, $private; |_entries, doc: String| {
$(
assert!(doc.contains($contains));
)*
$(
$(
assert!(!doc.contains($notcontains));
)*
)?
});
};
}
macro_rules! json_test {
( $name:ident, $source:expr; $json:tt ) => {
json_test!($name, $source, false; $json);
};
( $name:ident, $source:expr, private; $json:tt ) => {
json_test!($name, $source, true; $json);
};
( $name:ident, $source:expr, $private:expr; $json:tt ) => {
doc_test!($name, $source, $private; |entries, _doc| {
let actual = serde_json::to_value(&entries).unwrap();
let expected_json = json!($json);
pretty_assertions::assert_eq!(actual, expected_json);
});
};
}
#[tokio::test]
async fn content_type_handling() {
let sources = vec![(
"https://example.com/a",
Source::Module {
specifier: "https://example.com/a",
maybe_headers: Some(vec![(
"content-type",
"application/typescript; charset=utf-8",
)]),
content: r#"declare interface A {
a: string;
}"#,
},
)];
let mut memory_loader = MemoryLoader::new(sources, vec![]);
let root = ModuleSpecifier::parse("https://example.com/a").unwrap();
let analyzer = CapturingModuleAnalyzer::default();
let graph = create_type_graph(
vec![root.clone()],
&mut memory_loader,
GraphOptions {
module_analyzer: Some(&analyzer),
..Default::default()
},
)
.await;
let entries = DocParser::new(graph, false, analyzer.as_capturing_parser())
.parse_with_reexports(&root)
.unwrap();
assert_eq!(entries.len(), 1);
}
#[tokio::test]
async fn types_header_handling() {
let sources = vec![
(
"https://example.com/a.js",
Source::Module {
specifier: "https://example.com/a.js",
maybe_headers: Some(vec![
("content-type", "application/javascript; charset=utf-8"),
("x-typescript-types", "./a.d.ts"),
]),
content: r#"console.log("a");"#,
},
),
(
"https://example.com/a.d.ts",
Source::Module {
specifier: "https://example.com/a.d.ts",
maybe_headers: Some(vec![(
"content-type",
"application/typescript; charset=utf-8",
)]),
content: r#"export const a: "a";"#,
},
),
];
let mut memory_loader = MemoryLoader::new(sources, vec![]);
let root = ModuleSpecifier::parse("https://example.com/a.js").unwrap();
let analyzer = CapturingModuleAnalyzer::default();
let graph = create_type_graph(
vec![root.clone()],
&mut memory_loader,
GraphOptions {
module_analyzer: Some(&analyzer),
..Default::default()
},
)
.await;
let entries = DocParser::new(graph, false, analyzer.as_capturing_parser())
.parse_with_reexports(&root)
.unwrap();
assert_eq!(
serde_json::to_value(&entries).unwrap(),
json!([{
"kind": "variable",
"name": "a",
"location": {
"filename": "https://example.com/a.d.ts",
"line": 1,
"col": 13
},
"declarationKind": "export",
"variableDef": {
"tsType": {
"repr": "a",
"kind": "literal",
"literal": {
"kind": "string",
"string": "a"
}
},
"kind": "const"
}
}])
);
}
#[tokio::test]
async fn reexports() {
let nested_reexport_source_code = r#"
/**
* JSDoc for bar
*/
export const bar = "bar";
export default 42;
"#;
let reexport_source_code = r#"
import { bar } from "./nested_reexport.ts";
/**
* JSDoc for const
*/
export const foo = "foo";
export const fizz = "fizz";
"#;
let test_source_code = r#"
export { default, foo as fooConst } from "./reexport.ts";
import { fizz as buzz } from "./reexport.ts";
/** JSDoc for function */
export function fooFn(a: number) {
return a;
}
"#;
let (graph, analyzer, specifier) = setup(
"file:///test.ts",
vec![
("file:///test.ts", None, test_source_code),
("file:///reexport.ts", None, reexport_source_code),
(
"file:///nested_reexport.ts",
None,
nested_reexport_source_code,
),
],
)
.await;
let entries = DocParser::new(graph, false, analyzer.as_capturing_parser())
.parse_with_reexports(&specifier)
.unwrap();
assert_eq!(entries.len(), 3);
let expected_json = json!([
{
"kind": "variable",
"name": "fooConst",
"location": {
"filename": "file:///reexport.ts",
"line": 7,
"col": 13
},
"declarationKind": "export",
"jsDoc": {
"doc": "JSDoc for const",
},
"variableDef": {
"tsType": {
"repr": "foo",
"kind": "literal",
"literal": {
"kind": "string",
"string": "foo"
}
},
"kind": "const"
}
},
{
"kind": "function",
"name": "fooFn",
"location": {
"filename": "file:///test.ts",
"line": 6,
"col": 0
},
"declarationKind": "export",
"jsDoc": {
"doc": "JSDoc for function",
},
"functionDef": {
"params": [
{
"name": "a",
"kind": "identifier",
"optional": false,
"tsType": {
"keyword": "number",
"kind": "keyword",
"repr": "number",
},
}
],
"typeParams": [],
"returnType": null,
"hasBody": true,
"isAsync": false,
"isGenerator": false
},
},
{
"kind": "import",
"name": "buzz",
"location": {
"filename": "file:///test.ts",
"line": 3,
"col": 0
},
"declarationKind": "private",
"importDef": {
"src": "file:///reexport.ts",
"imported": "fizz",
}
}
]);
let actual = serde_json::to_value(&entries).unwrap();
assert_eq!(actual, expected_json);
assert!(DocPrinter::new(&entries, false, false)
.to_string()
.as_str()
.contains("function fooFn(a: number)"));
}
#[tokio::test]
async fn reexports_has_same_name() {
let reexport_source_code = r#"
export interface Hello {}
export class Hello {}
"#;
let test_source_code = r#"
export { Hello } from "./reexport.ts";
"#;
let (graph, analyzer, specifier) = setup(
"file:///test.ts",
vec![
("file:///test.ts", None, test_source_code),
("file:///reexport.ts", None, reexport_source_code),
],
)
.await;
let entries = DocParser::new(graph, false, analyzer.as_capturing_parser())
.parse_with_reexports(&specifier)
.unwrap();
assert_eq!(entries.len(), 2);
let expected_json = json!([
{
"kind": "interface",
"name": "Hello",
"location": {
"filename": "file:///reexport.ts",
"line": 2,
"col": 0
},
"declarationKind": "export",
"interfaceDef": {
"extends": [],
"methods": [],
"properties": [],
"callSignatures": [],
"indexSignatures": [],
"typeParams": []
}
},
{
"kind": "class",
"name": "Hello",
"location": {
"filename": "file:///reexport.ts",
"line": 3,
"col": 0
},
"declarationKind": "export",
"classDef": {
"isAbstract": false,
"constructors": [],
"properties": [],
"indexSignatures": [],
"methods": [],
"extends": null,
"implements": [],
"typeParams": [],
"superTypeParams": []
}
}
]);
let actual = serde_json::to_value(&entries).unwrap();
assert_eq!(actual, expected_json);
let output = DocPrinter::new(&entries, false, false).to_string();
assert!(output.contains("class Hello"));
assert!(output.contains("interface Hello"));
}
#[tokio::test]
async fn deep_reexports() {
let foo_source_code = r#"export const foo: string = "foo";"#;
let bar_source_code = r#"export * from "./foo.ts""#;
let baz_source_code = r#"export * from "./bar.ts""#;
let (graph, analyzer, specifier) = setup(
"file:///baz.ts",
vec![
("file:///foo.ts", None, foo_source_code),
("file:///bar.ts", None, bar_source_code),
("file:///baz.ts", None, baz_source_code),
],
)
.await;
let entries = DocParser::new(graph, false, analyzer.as_capturing_parser())
.parse_with_reexports(&specifier)
.unwrap();
assert_eq!(entries.len(), 1);
let expected_json = json!([
{
"kind": "variable",
"name": "foo",
"location": {
"filename": "file:///foo.ts",
"line": 1,
"col": 13
},
"declarationKind": "export",
"variableDef": {
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
},
"kind": "const"
}
}
]);
let actual = serde_json::to_value(&entries).unwrap();
assert_eq!(actual, expected_json);
assert!(DocPrinter::new(&entries, false, false)
.to_string()
.contains("const foo"))
}
#[tokio::test]
async fn reexport_module_doc() {
let mod_doc_source_code = r#"
/**
* This is some module doc.
*
* @module
*/
/** a variable */
export const a = "a";
"#;
let ns_source_code = r#"
export * as b from "./mod_doc.ts";
"#;
let (graph, analyzer, specifier) = setup(
"file:///ns.ts",
vec![
("file:///ns.ts", None, ns_source_code),
("file:///mod_doc.ts", None, mod_doc_source_code),
],
)
.await;
let entries = DocParser::new(graph, false, analyzer.as_capturing_parser())
.parse_with_reexports(&specifier)
.unwrap();
let actual = serde_json::to_value(&entries).unwrap();
let expected = json!([
{
"kind": "namespace",
"name": "b",
"location": {
"filename": "./mod_doc.ts",
"line": 1,
"col": 0
},
"declarationKind": "export",
"jsDoc": {
"doc": "This is some module doc.\n",
"tags": [
{
"kind": "module"
}
]
},
"namespaceDef": {
"elements": [
{
"kind": "variable",
"name": "a",
"location": {
"filename": "file:///mod_doc.ts",
"line": 9,
"col": 13
},
"declarationKind": "export",
"jsDoc": {
"doc": "a variable"
},
"variableDef": {
"tsType": {
"repr": "a",
"kind": "literal",
"literal": {
"kind": "string",
"string": "a"
}
},
"kind": "const"
}
}
]
}
}
]);
assert_eq!(actual, expected);
}
#[tokio::test]
async fn filter_nodes_by_name() {
use crate::find_nodes_by_name_recursively;
use crate::DocNodeKind;
let source_code = r#"
export namespace Deno {
export class Buffer {}
export function test(options: object): void;
export function test(name: string, fn: Function): void;
export function test(name: string | object, fn?: Function): void {}
}
export namespace Deno {
export namespace Inner {
export function a(): void {}
export const b = 100;
}
export interface Conn {
rid: number;
closeWrite(): void;
}
export class Process {
readonly pid: number;
output(): Promise<Uint8Array>;
}
}
"#;
let (graph, analyzer, specifier) = setup(
"file:///test.ts",
vec![("file:///test.ts", None, source_code)],
)
.await;
let entries = DocParser::new(graph, false, analyzer.as_capturing_parser())
.parse(&specifier)
.unwrap();
let found =
find_nodes_by_name_recursively(entries.clone(), "Deno".to_string());
assert_eq!(found.len(), 2);
assert_eq!(found[0].name, "Deno".to_string());
assert_eq!(found[1].name, "Deno".to_string());
let found =
find_nodes_by_name_recursively(entries.clone(), "Deno.test".to_string());
assert_eq!(found.len(), 3);
assert_eq!(found[0].name, "test".to_string());
assert_eq!(found[1].name, "test".to_string());
assert_eq!(found[2].name, "test".to_string());
let found =
find_nodes_by_name_recursively(entries.clone(), "Deno.Inner.a".to_string());
assert_eq!(found.len(), 1);
assert_eq!(found[0].name, "a".to_string());
let found = find_nodes_by_name_recursively(
entries.clone(),
"Deno.Conn.rid".to_string(),
);
assert_eq!(found.len(), 1);
assert_eq!(found[0].name, "rid".to_string());
assert_eq!(found[0].kind, DocNodeKind::Variable);
let found = find_nodes_by_name_recursively(
entries.clone(),
"Deno.Conn.closeWrite".to_string(),
);
assert_eq!(found.len(), 1);
assert_eq!(found[0].name, "closeWrite".to_string());
assert_eq!(found[0].kind, DocNodeKind::Function);
let found = find_nodes_by_name_recursively(
entries.clone(),
"Deno.Process.pid".to_string(),
);
assert_eq!(found.len(), 1);
assert_eq!(found[0].name, "pid".to_string());
assert_eq!(found[0].kind, DocNodeKind::Variable);
let found = find_nodes_by_name_recursively(
entries.clone(),
"Deno.Process.output".to_string(),
);
assert_eq!(found.len(), 1);
assert_eq!(found[0].name, "output".to_string());
assert_eq!(found[0].kind, DocNodeKind::Function);
let found =
find_nodes_by_name_recursively(entries.clone(), "Deno.test.a".to_string());
assert_eq!(found.len(), 0);
let found = find_nodes_by_name_recursively(entries, "a.b.c".to_string());
assert_eq!(found.len(), 0);
}
#[tokio::test]
async fn exports_imported_earlier() {
let foo_source_code = r#"export const foo: string = "foo";"#;
let test_source_code = r#"
import { foo } from "./foo.ts";
export { foo };
"#;
let (graph, analyzer, specifier) = setup(
"file:///test.ts",
vec![
("file:///foo.ts", None, foo_source_code),
("file:///test.ts", None, test_source_code),
],
)
.await;
let entries = DocParser::new(graph, false, analyzer.as_capturing_parser())
.parse_with_reexports(&specifier)
.unwrap();
assert_eq!(entries.len(), 2);
let expected_json = json!([
{
"kind": "variable",
"name": "foo",
"location": {
"filename": "file:///foo.ts",
"line": 1,
"col": 13
},
"declarationKind": "export",
"variableDef": {
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
},
"kind": "const"
}
},
{
"kind": "import",
"name": "foo",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 2,
},
"declarationKind": "private",
"importDef": {
"src": "file:///foo.ts",
"imported": "foo",
},
},
]);
let actual = serde_json::to_value(&entries).unwrap();
assert_eq!(actual, expected_json);
}
#[tokio::test]
async fn exports_imported_earlier_renamed() {
let foo_source_code = r#"export const foo: string = "foo";"#;
let test_source_code = r#"
import { foo as f } from "./foo.ts";
export { f };
"#;
let (graph, analyzer, specifier) = setup(
"file:///test.ts",
vec![
("file:///foo.ts", None, foo_source_code),
("file:///test.ts", None, test_source_code),
],
)
.await;
let entries = DocParser::new(graph, false, analyzer.as_capturing_parser())
.parse_with_reexports(&specifier)
.unwrap();
assert_eq!(entries.len(), 2);
let expected_json = json!([
{
"kind": "variable",
"name": "f",
"location": {
"filename": "file:///foo.ts",
"line": 1,
"col": 13
},
"declarationKind": "export",
"variableDef": {
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
},
"kind": "const"
}
},
{
"kind": "import",
"name": "f",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 2
},
"declarationKind": "private",
"importDef": {
"src": "file:///foo.ts",
"imported": "foo"
}
}
]);
let actual = serde_json::to_value(&entries).unwrap();
assert_eq!(actual, expected_json);
}
#[tokio::test]
async fn exports_imported_earlier_default() {
let foo_source_code = r#"const foo: string = "foo";
export default foo;"#;
let test_source_code = r#"
import foo from "./foo.ts";
export { foo };
"#;
let (graph, analyzer, specifier) = setup(
"file:///test.ts",
vec![
("file:///foo.ts", None, foo_source_code),
("file:///test.ts", None, test_source_code),
],
)
.await;
let entries = DocParser::new(graph, false, analyzer.as_capturing_parser())
.parse_with_reexports(&specifier)
.unwrap();
assert_eq!(entries.len(), 2);
let expected_json = json!([
{
"kind": "variable",
"name": "foo",
"location": {
"filename": "file:///foo.ts",
"line": 1,
"col": 0
},
"declarationKind": "export",
"variableDef": {
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
},
"kind": "const"
}
},
{
"kind": "import",
"name": "foo",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 2
},
"declarationKind": "private",
"importDef": {
"src": "file:///foo.ts",
"imported": "default"
}
}
]);
let actual = serde_json::to_value(&entries).unwrap();
assert_eq!(actual, expected_json);
}
#[tokio::test]
async fn exports_imported_earlier_private() {
let foo_source_code = r#"export const foo: string = "foo";"#;
let test_source_code = r#"
import { foo } from "./foo.ts";
export { foo };
"#;
let (graph, analyzer, specifier) = setup(
"file:///test.ts",
vec![
("file:///foo.ts", None, foo_source_code),
("file:///test.ts", None, test_source_code),
],
)
.await;
let entries = DocParser::new(graph, true, analyzer.as_capturing_parser())
.parse_with_reexports(&specifier)
.unwrap();
let expected_json = json!([
{
"kind": "variable",
"name": "foo",
"location": {
"filename": "file:///foo.ts",
"line": 1,
"col": 13
},
"declarationKind": "export",
"variableDef": {
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
},
"kind": "const"
}
},
{
"kind": "import",
"name": "foo",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 2,
},
"declarationKind": "private",
"importDef": {
"src": "file:///foo.ts",
"imported": "foo",
},
},
]);
let actual = serde_json::to_value(&entries).unwrap();
assert_eq!(actual, expected_json);
}
#[tokio::test]
async fn variable_syntax() {
let (graph, analyzer, specifier) = setup(
"file:///foo.ts",
vec![
("file:///foo.ts", None, "export * from './bar.tsx'"),
("file:///bar.tsx", None, "export default <foo>bar</foo>"),
],
)
.await;
DocParser::new(graph, false, analyzer.as_capturing_parser())
.parse_with_reexports(&specifier)
.unwrap();
}
#[tokio::test]
async fn json_module() {
let (graph, analyzer, specifier) = setup(
"file:///foo.ts",
vec![
("file:///foo.ts", None, "export { default as configFile } from './bar.json' assert { type: 'json' };"),
("file:///bar.json", None, r#"{ "a": 5, "b": "text", "c": null, "d": [1, 2], "e": { "a": 1 } }"#),
],
)
.await;
let entries = DocParser::new(graph, false, analyzer.as_capturing_parser())
.parse_with_reexports(&specifier)
.unwrap();
let expected_json = json!([
{
"kind": "variable",
"name": "configFile",
"location": {
"filename": "file:///bar.json",
"line": 1,
"col": 0
},
"declarationKind": "export",
"variableDef": {
"tsType": {
"repr": "",
"kind": "typeLiteral",
"typeLiteral": {
"methods": [],
"properties": [{
"name": "a",
"params": [],
"computed": false,
"optional": false,
"tsType": {
"repr": "5",
"kind": "literal",
"literal": {
"kind": "number",
"number": 5.0,
},
},
"typeParams": []
}, {
"name": "b",
"params": [],
"computed": false,
"optional": false,
"tsType": {
"repr": "text",
"kind": "literal",
"literal": {
"kind": "string",
"string": "text",
},
},
"typeParams": []
}, {
"name": "c",
"params": [],
"computed": false,
"optional": false,
"tsType": {
"repr": "null",
"kind": "keyword",
"keyword": "null",
},
"typeParams": []
}, {
"name": "d",
"params": [],
"computed": false,
"optional": false,
"tsType": {
"repr": "unknown[]",
"kind": "array",
"array": {
"repr": "unknown",
"kind": "keyword",
"keyword": "unknown",
},
},
"typeParams": []
}, {
"name": "e",
"params": [],
"computed": false,
"optional": false,
"tsType": {
"repr": "",
"kind": "typeLiteral",
"typeLiteral": {
"methods": [],
"properties": [{
"name": "a",
"params": [],
"computed": false,
"optional": false,
"tsType": {
"repr": "1",
"kind": "literal",
"literal": {
"kind": "number",
"number": 1.0,
},
},
"typeParams": []
}],
"callSignatures": [],
"indexSignatures": [],
},
},
"typeParams": []
}],
"callSignatures": [],
"indexSignatures": [],
},
},
"kind": "var",
},
},
]);
let actual = serde_json::to_value(&entries).unwrap();
assert_eq!(actual, expected_json);
}
mod serialization {
use crate::*;
json_test!(module_docs,
r#"/* a non-jsdoc comment */
/**
* A leading JSDoc comment
* @module
*/
/** One associated with a class */
export class A {}
"#;
[
{
"kind": "moduleDoc",
"name": "",
"location": {
"filename": "file:///test.ts",
"line": 3,
"col": 0,
},
"declarationKind": "export",
"jsDoc": {
"doc": "A leading JSDoc comment",
"tags": [{
"kind": "module"
}],
}
}, {
"kind": "class",
"name": "A",
"location": {
"filename": "file:///test.ts",
"line": 9,
"col": 0,
},
"declarationKind": "export",
"jsDoc": {
"doc": "One associated with a class",
},
"classDef": {
"isAbstract": false,
"constructors": [],
"properties": [],
"indexSignatures": [],
"methods": [],
"extends": null,
"implements": [],
"typeParams": [],
"superTypeParams": []
}
}
]);
json_test!(declare_namespace,
r#"
/** Namespace JSdoc */
declare namespace RootNs {
declare const a = "a";
/** Nested namespace JSDoc */
declare namespace NestedNs {
declare enum Foo {
a = 1,
b = 2,
c = 3,
}
}
}
"#;
[{
"kind": "namespace",
"name": "RootNs",
"location": {
"filename": "file:///test.ts",
"line": 3,
"col": 0
},
"declarationKind": "declare",
"jsDoc": {
"doc": "Namespace JSdoc",
},
"namespaceDef": {
"elements": [
{
"kind": "variable",
"name": "a",
"location": {
"filename": "file:///test.ts",
"line": 4,
"col": 4
},
"declarationKind": "declare",
"variableDef": {
"tsType": {
"repr": "a",
"kind": "literal",
"literal": {
"kind": "string",
"string": "a"
}
},
"kind": "const"
}
},
{
"kind": "namespace",
"name": "NestedNs",
"location": {
"filename": "file:///test.ts",
"line": 7,
"col": 4
},
"jsDoc": {
"doc": "Nested namespace JSDoc",
},
"declarationKind": "declare",
"namespaceDef": {
"elements": [
{
"kind": "enum",
"name": "Foo",
"location": {
"filename": "file:///test.ts",
"line": 8,
"col": 6
},
"declarationKind": "declare",
"enumDef": {
"members": [
{
"name": "a",
"init": {
"repr": "1",
"kind": "literal",
"literal": {
"kind": "number",
"number": 1.0,
}
},
"location": {
"filename": "file:///test.ts",
"line": 9,
"col": 8,
},
},
{
"name": "b",
"init": {
"repr": "2",
"kind": "literal",
"literal": {
"kind": "number",
"number": 2.0,
}
},
"location": {
"filename": "file:///test.ts",
"line": 10,
"col": 8,
},
},
{
"name": "c",
"init": {
"repr": "3",
"kind": "literal",
"literal": {
"kind": "number",
"number": 3.0,
}
},
"location": {
"filename": "file:///test.ts",
"line": 11,
"col": 8,
},
}
]
}
}
]
}
}
]
}
}]);
json_test!(structured_jsdoc,
r#"
/** Class doc */
export class A {
/** @private */
p = false;
/**
* Some leading documentation here.
*
* @param {string} name some comment
* @param {string} a some other comment that
* spans two lines
* @param {number} b a number
*/
constructor(name, a, b) {}
/**
* @returns {Promise<void>}
*/
a() {}
}
"#;
[{
"kind": "class",
"name": "A",
"location": {
"filename": "file:///test.ts",
"line": 3,
"col": 0,
},
"jsDoc": {
"doc": "Class doc",
},
"declarationKind": "export",
"classDef": {
"isAbstract": false,
"constructors": [
{
"jsDoc": {
"doc": "Some leading documentation here.\n",
"tags": [
{
"kind": "param",
"name": "name",
"type": "string",
"doc": "some comment",
},
{
"kind": "param",
"name": "a",
"type": "string",
"doc": "some other comment that\nspans two lines",
},
{
"kind": "param",
"name": "b",
"type": "number",
"doc": "a number",
},
],
},
"accessibility": null,
"hasBody": true,
"name": "constructor",
"params": [
{
"kind": "identifier",
"name": "name",
"optional": false,
"tsType": null,
},
{
"kind": "identifier",
"name": "a",
"optional": false,
"tsType": null,
},
{
"kind": "identifier",
"name": "b",
"optional": false,
"tsType": null,
}
],
"location": {
"filename": "file:///test.ts",
"line": 15,
"col": 2,
}
}
],
"properties": [
{
"jsDoc": {
"tags": [
{
"kind": "private"
}
]
},
"tsType": {
"repr": "boolean",
"kind": "keyword",
"keyword": "boolean",
},
"readonly": false,
"accessibility": null,
"optional": false,
"isAbstract": false,
"isStatic": false,
"name": "p",
"location": {
"filename": "file:///test.ts",
"line": 5,
"col": 2,
}
}
],
"indexSignatures": [],
"methods": [
{
"jsDoc": {
"tags": [
{
"kind": "return",
"type": "Promise<void>"
}
]
},
"accessibility": null,
"optional": false,
"isAbstract": false,
"isStatic": false,
"name": "a",
"kind": "method",
"functionDef": {
"params": [],
"returnType": null,
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": [],
},
"location": {
"filename": "file:///test.ts",
"line": 20,
"col": 2,
}
}
],
"extends": null,
"implements": [],
"typeParams": [],
"superTypeParams": [],
}
}]);
json_test!(export_class,
r#"
/** Class doc */
export class Foobar extends Fizz implements Buzz, Aldrin {
private private1?: boolean;
protected protected1: number;
public public1: boolean;
public2: number;
override public3: string;
m = new Map<string, string>();
s = "foo";
a = [1, "foo"];
fn = (a: string): void => {};
/** Constructor js doc */
constructor(name: string, private private2: number, protected protected2: number) {}
/** Async foo method */
async foo(): Promise<void> {
//
}
/** Sync bar method */
bar?(): void {
//
}
override s() {}
}
"#;
[{
"kind": "class",
"name": "Foobar",
"location": {
"filename": "file:///test.ts",
"line": 3,
"col": 0
},
"declarationKind": "export",
"jsDoc": {
"doc": "Class doc",
},
"classDef": {
"isAbstract": false,
"extends": "Fizz",
"implements": [
{
"repr": "Buzz",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "Buzz"
}
},
{
"repr": "Aldrin",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "Aldrin"
}
}
],
"typeParams": [],
"superTypeParams": [],
"constructors": [
{
"jsDoc": {
"doc": "Constructor js doc",
},
"accessibility": null,
"hasBody": true,
"name": "constructor",
"params": [
{
"name": "name",
"kind": "identifier",
"optional": false,
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
}
},
{
"accessibility": "private",
"name": "private2",
"kind": "identifier",
"optional": false,
"tsType": {
"repr": "number",
"kind": "keyword",
"keyword": "number"
}
},
{
"accessibility": "protected",
"name": "protected2",
"kind": "identifier",
"optional": false,
"tsType": {
"repr": "number",
"kind": "keyword",
"keyword": "number"
}
}
],
"location": {
"filename": "file:///test.ts",
"line": 15,
"col": 4
}
}
],
"properties": [
{
"tsType": {
"repr": "boolean",
"kind": "keyword",
"keyword": "boolean"
},
"readonly": false,
"accessibility": "private",
"optional": true,
"isAbstract": false,
"isStatic": false,
"name": "private1",
"location": {
"filename": "file:///test.ts",
"line": 4,
"col": 4
}
},
{
"tsType": {
"repr": "number",
"kind": "keyword",
"keyword": "number"
},
"readonly": false,
"accessibility": "protected",
"optional": false,
"isAbstract": false,
"isStatic": false,
"name": "protected1",
"location": {
"filename": "file:///test.ts",
"line": 5,
"col": 4
}
},
{
"tsType": {
"repr": "boolean",
"kind": "keyword",
"keyword": "boolean"
},
"readonly": false,
"accessibility": "public",
"optional": false,
"isAbstract": false,
"isStatic": false,
"name": "public1",
"location": {
"filename": "file:///test.ts",
"line": 6,
"col": 4
}
},
{
"tsType": {
"repr": "number",
"kind": "keyword",
"keyword": "number"
},
"readonly": false,
"accessibility": null,
"optional": false,
"isAbstract": false,
"isStatic": false,
"name": "public2",
"location": {
"filename": "file:///test.ts",
"line": 7,
"col": 4
}
},
{
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
},
"readonly": false,
"accessibility": null,
"optional": false,
"isAbstract": false,
"isStatic": false,
"isOverride": true,
"name": "public3",
"location": {
"filename": "file:///test.ts",
"line": 8,
"col": 4
}
},
{
"tsType": {
"repr": "Map",
"kind": "typeRef",
"typeRef": {
"typeParams": [
{
"repr": "string",
"kind": "keyword",
"keyword": "string"
},
{
"repr": "string",
"kind": "keyword",
"keyword": "string"
}
],
"typeName": "Map",
},
},
"readonly": false,
"accessibility": null,
"optional": false,
"isAbstract": false,
"isStatic": false,
"name": "m",
"location": {
"filename": "file:///test.ts",
"line": 9,
"col": 4,
}
},
{
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string",
},
"readonly": false,
"accessibility": null,
"optional": false,
"isAbstract": false,
"isStatic": false,
"name": "s",
"location": {
"filename": "file:///test.ts",
"line": 10,
"col": 4,
}
},
{
"tsType": {
"repr": "",
"kind": "array",
"array": {
"repr": "",
"kind": "union",
"union": [
{
"repr": "number",
"kind": "keyword",
"keyword": "number",
}, {
"repr": "string",
"kind": "keyword",
"keyword": "string",
}
]
}
},
"readonly": false,
"accessibility": null,
"optional": false,
"isAbstract": false,
"isStatic": false,
"name": "a",
"location": {
"filename": "file:///test.ts",
"line": 11,
"col": 4,
}
},
{
"tsType": {
"repr": "",
"kind": "fnOrConstructor",
"fnOrConstructor": {
"constructor": false,
"tsType": {
"repr": "void",
"kind": "keyword",
"keyword": "void"
},
"params": [
{
"kind": "identifier",
"name": "a",
"optional": false,
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string",
}
}
],
"typeParams": []
}
},
"readonly": false,
"accessibility": null,
"optional": false,
"isAbstract": false,
"isStatic": false,
"name": "fn",
"location": {
"filename": "file:///test.ts",
"line": 12,
"col": 4,
},
}
],
"indexSignatures": [],
"methods": [
{
"jsDoc": {
"doc": "Async foo method",
},
"accessibility": null,
"optional": false,
"isAbstract": false,
"isStatic": false,
"name": "foo",
"kind": "method",
"functionDef": {
"params": [],
"returnType": {
"repr": "Promise",
"kind": "typeRef",
"typeRef": {
"typeParams": [
{
"repr": "void",
"kind": "keyword",
"keyword": "void"
}
],
"typeName": "Promise"
}
},
"typeParams": [],
"hasBody": true,
"isAsync": true,
"isGenerator": false
},
"location": {
"filename": "file:///test.ts",
"line": 18,
"col": 4
}
},
{
"jsDoc": {
"doc": "Sync bar method",
},
"accessibility": null,
"optional": true,
"isAbstract": false,
"isStatic": false,
"name": "bar",
"kind": "method",
"functionDef": {
"params": [],
"returnType": {
"repr": "void",
"kind": "keyword",
"keyword": "void"
},
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": []
},
"location": {
"filename": "file:///test.ts",
"line": 23,
"col": 4
}
},
{
"accessibility": null,
"optional": false,
"isAbstract": false,
"isStatic": false,
"isOverride": true,
"name": "s",
"kind": "method",
"functionDef": {
"params": [],
"returnType": null,
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": [],
},
"location": {
"filename": "file:///test.ts",
"line": 27,
"col": 4,
}
}
]
}
}]);
json_test!(export_class_decorators,
r#"
@sealed
export class A {
#x = "x";
@format("Hello, %s")
greeting: string;
@configurable(false)
get x() {
return this.#x;
}
@enumerable(false)
greet() {
return "hello";
}
method(@readonly param: string) {}
}
"#;
[{
"kind": "class",
"name": "A",
"location": {
"filename": "file:///test.ts",
"line": 3,
"col": 0,
},
"declarationKind": "export",
"classDef": {
"isAbstract": false,
"constructors": [],
"properties": [
{
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string",
},
"readonly": false,
"accessibility": null,
"decorators": [
{
"name": "format",
"args": [
"\"Hello, %s\"",
],
"location": {
"filename": "file:///test.ts",
"line": 6,
"col": 3,
}
}
],
"optional": false,
"isAbstract": false,
"isStatic": false,
"name": "greeting",
"location": {
"filename": "file:///test.ts",
"line": 6,
"col": 2,
}
}
],
"indexSignatures": [],
"methods": [
{
"accessibility": null,
"optional": false,
"isAbstract": false,
"isStatic": false,
"name": "x",
"kind": "getter",
"functionDef": {
"params": [],
"returnType": null,
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": [],
"decorators": [
{
"name": "configurable",
"args": [
"false"
],
"location": {
"filename": "file:///test.ts",
"line": 9,
"col": 3,
}
}
]
},
"location": {
"filename": "file:///test.ts",
"line": 9,
"col": 2,
}
}, {
"accessibility": null,
"optional": false,
"isAbstract": false,
"isStatic": false,
"name": "greet",
"kind": "method",
"functionDef": {
"params": [],
"returnType": null,
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": [],
"decorators": [
{
"name": "enumerable",
"args": [
"false"
],
"location": {
"filename": "file:///test.ts",
"line": 14,
"col": 3,
}
}
]
},
"location": {
"filename": "file:///test.ts",
"line": 14,
"col": 2,
}
}, {
"accessibility": null,
"optional": false,
"isAbstract": false,
"isStatic": false,
"name": "method",
"kind": "method",
"functionDef": {
"params": [
{
"kind": "identifier",
"name": "param",
"optional": false,
"decorators": [
{
"name": "readonly",
"location": {
"filename": "file:///test.ts",
"line": 19,
"col": 10,
}
}
],
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string",
}
}
],
"returnType": null,
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": [],
},
"location": {
"filename": "file:///test.ts",
"line": 19,
"col": 2,
}
}
],
"extends": null,
"implements": [],
"typeParams": [],
"superTypeParams": [],
"decorators": [
{
"name": "sealed",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 1,
}
}
]
}
}]
);
json_test!(decorators_jsdoc,
r#"
/** jsdoc */
@sealed
export class A {
a: string = "a";
}
"#;
[
{
"kind": "class",
"name": "A",
"location": {
"filename": "file:///test.ts",
"line": 4,
"col": 0,
},
"declarationKind": "export",
"jsDoc": {
"doc": "jsdoc"
},
"classDef": {
"isAbstract": false,
"constructors": [],
"properties": [
{
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string",
},
"readonly": false,
"accessibility": null,
"optional": false,
"isAbstract": false,
"isStatic": false,
"name": "a",
"location": {
"filename": "file:///test.ts",
"line": 5,
"col": 2,
}
}
],
"indexSignatures": [],
"methods": [],
"extends": null,
"implements": [],
"typeParams": [],
"superTypeParams": [],
"decorators": [
{
"name": "sealed",
"location": {
"filename": "file:///test.ts",
"line": 3,
"col": 1,
}
}
]
}
}
]);
json_test!(export_const,
r#"
/** Something about fizzBuzz */
export const fizzBuzz = "fizzBuzz";
export const env: {
/** get doc */
get(key: string): string | undefined;
/** set doc */
set(key: string, value: string): void;
}
export const num = 100;
export const bool = false;
export const bigint = 123n;
export const regex = /hello/;
export const date = new Date();
export const tpl1 = `foo`;
export const tpl2 = `Value: ${num}`;
"#;
[
{
"kind":"variable",
"name":"fizzBuzz",
"location":{
"filename":"file:///test.ts",
"line":3,
"col":13
},
"declarationKind": "export",
"jsDoc": {
"doc": "Something about fizzBuzz",
},
"variableDef":{
"tsType": {
"repr": "fizzBuzz",
"kind": "literal",
"literal": {
"kind": "string",
"string": "fizzBuzz"
}
},
"kind":"const"
}
},
{
"kind":"variable",
"name":"env",
"location":{
"filename":"file:///test.ts",
"line":5,
"col":13
},
"declarationKind": "export",
"variableDef":{
"tsType":{
"repr":"",
"kind":"typeLiteral",
"typeLiteral":{
"methods":[{
"name":"get",
"kind": "method",
"params":[
{
"name":"key",
"kind":"identifier",
"optional":false,
"tsType":{
"repr":"string",
"kind":"keyword",
"keyword":"string"
}
}
],
"optional": false,
"returnType":{
"repr":"",
"kind":"union",
"union":[
{
"repr":"string",
"kind":"keyword",
"keyword":"string"
},
{
"repr":"undefined",
"kind":"keyword",
"keyword":"undefined"
}
]
},
"typeParams":[]
}, {
"name":"set",
"kind": "method",
"params":[
{
"name":"key",
"kind":"identifier",
"optional":false,
"tsType":{
"repr":"string",
"kind":"keyword",
"keyword":"string"
}
},
{
"name":"value",
"kind":"identifier",
"optional":false,
"tsType":{
"repr":"string",
"kind":"keyword",
"keyword":"string"
}
}
],
"optional": false,
"returnType":{
"repr":"void",
"kind":"keyword",
"keyword":"void"
},
"typeParams":[]
}
],
"properties":[],
"callSignatures":[],
"indexSignatures": []
}
},
"kind":"const"
}
},
{
"kind":"variable",
"name":"num",
"location": {
"filename":"file:///test.ts",
"line":13,
"col":13
},
"declarationKind": "export",
"variableDef":{
"tsType":{
"repr":"100",
"kind":"literal",
"literal":{
"kind":"number",
"number":100.0
}
},
"kind":"const"
}
},
{
"kind":"variable",
"name":"bool",
"location":{
"filename":"file:///test.ts",
"line":14,
"col":13
},
"declarationKind": "export",
"variableDef":{
"tsType":{
"repr":"false",
"kind":"literal",
"literal":{
"kind":"boolean",
"boolean":false
}
},
"kind":"const"
}
},
{
"kind":"variable",
"name":"bigint",
"location":{
"filename":"file:///test.ts",
"line":15,
"col":13
},
"declarationKind": "export",
"variableDef":{
"tsType":{
"repr":"123",
"kind":"literal",
"literal":{
"kind":"bigInt",
"string":"123"
}
},
"kind":"const"
}
},
{
"kind":"variable",
"name":"regex",
"location":{
"filename":"file:///test.ts",
"line":16,
"col":13
},
"declarationKind": "export",
"variableDef":{
"tsType":{
"repr": "hello",
"kind":"typeRef",
"typeRef":{
"typeParams":null,
"typeName":"RegExp"
}
},
"kind":"const"
}
},
{
"kind":"variable",
"name":"date",
"location":{
"filename":"file:///test.ts",
"line":17,
"col":13
},
"declarationKind": "export",
"variableDef":{
"tsType":{
"repr": "Date",
"kind":"typeRef",
"typeRef":{
"typeParams":null,
"typeName":"Date"
}
},
"kind":"const"
}
},
{
"kind":"variable",
"name":"tpl1",
"location":{
"filename":"file:///test.ts",
"line":18,
"col":13
},
"declarationKind": "export",
"variableDef":{
"tsType":{
"repr": "foo",
"kind":"literal",
"literal":{
"kind":"template",
"tsTypes": [
{
"repr": "foo",
"kind": "literal",
"literal": {
"kind": "string",
"string": "foo"
}
}
]
}
},
"kind":"const"
}
},
{
"kind":"variable",
"name":"tpl2",
"location":{
"filename":"file:///test.ts",
"line":19,
"col":13
},
"declarationKind": "export",
"variableDef":{
"tsType":{
"repr": "string",
"kind":"keyword",
"keyword":"string"
},
"kind":"const"
}
}
]
);
json_test!(export_const_destructured,
r#"const c = { a: "a", b: 2 };
const d: { f: string; g: number; } = { f: "f", g: 2 };
export const {
/** export a doc */
a,
/** export b doc */
b,
}: {
/** type alias doc */
a: string;
/** other doc */
b: number;
} = c;
export const { f, g: h } = d;
"#;
[
{
"kind": "variable",
"name": "a",
"location": {
"filename": "file:///test.ts",
"line": 6,
"col": 6,
},
"declarationKind": "export",
"jsDoc": {
"doc": "export a doc",
},
"variableDef": {
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
},
"kind": "const"
}
},
{
"kind": "variable",
"name": "b",
"location": {
"filename": "file:///test.ts",
"line": 8,
"col": 6
},
"declarationKind": "export",
"jsDoc": {
"doc": "export b doc",
},
"variableDef": {
"tsType": {
"repr": "number",
"kind": "keyword",
"keyword": "number"
},
"kind": "const"
}
},
{
"kind": "variable",
"name": "f",
"location": {
"filename": "file:///test.ts",
"line": 16,
"col": 19
},
"declarationKind": "export",
"variableDef": {
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
},
"kind": "const"
}
},
{
"kind": "variable",
"name": "h",
"location": {
"filename": "file:///test.ts",
"line": 16,
"col": 11
},
"declarationKind": "export",
"variableDef": {
"tsType": {
"repr": "number",
"kind": "keyword",
"keyword": "number"
},
"kind": "const"
}
}
]
);
json_test!(export_let,
r#"
export let str = "hello";
export let num = 100;
export let bool = false;
export let dateStr = Date();
export let regex = RegExp("foobar");
export let sym = Symbol("baz");
export let tpl = `foobarbaz`;
"#;
[
{
"kind":"variable",
"name":"str",
"location":{
"filename":"file:///test.ts",
"line":2,
"col":11
},
"declarationKind": "export",
"variableDef":{
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword":"string"
},
"kind":"let"
}
},
{
"kind":"variable",
"name":"num",
"location":{
"filename":"file:///test.ts",
"line":3,
"col":11
},
"declarationKind": "export",
"variableDef":{
"tsType": {
"repr": "number",
"kind": "keyword",
"keyword":"number"
},
"kind":"let"
}
},
{
"kind":"variable",
"name":"bool",
"location":{
"filename":"file:///test.ts",
"line":4,
"col":11
},
"declarationKind": "export",
"variableDef":{
"tsType": {
"repr": "boolean",
"kind": "keyword",
"keyword":"boolean"
},
"kind":"let"
}
},
{
"kind":"variable",
"name":"dateStr",
"location":{
"filename":"file:///test.ts",
"line":5,
"col":11
},
"declarationKind": "export",
"variableDef":{
"tsType": {
"repr": "Date",
"kind": "keyword",
"keyword":"string"
},
"kind":"let"
}
},
{
"kind":"variable",
"name":"regex",
"location":{
"filename":"file:///test.ts",
"line":6,
"col":11
},
"declarationKind": "export",
"variableDef":{
"tsType": {
"repr": "RegExp",
"kind": "typeRef",
"typeRef":{
"typeParams":null,
"typeName":"RegExp"
}
},
"kind":"let"
}
},
{
"kind":"variable",
"name":"sym",
"location":{
"filename":"file:///test.ts",
"line":7,
"col":11
},
"declarationKind": "export",
"variableDef":{
"tsType": {
"repr": "Symbol",
"kind": "keyword",
"keyword":"symbol"
},
"kind":"let"
}
},
{
"kind":"variable",
"name":"tpl",
"location":{
"filename":"file:///test.ts",
"line":8,
"col":11
},
"declarationKind": "export",
"variableDef":{
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword":"string"
},
"kind":"let"
}
}
]
);
json_test!(export_class_ctor_properties,
r#"
export class A {
constructor(public readonly name: string, private private: number, public override public: boolean) {}
}"#;
[{
"kind": "class",
"name": "A",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 0,
},
"declarationKind": "export",
"classDef": {
"isAbstract": false,
"constructors": [{
"accessibility": null,
"hasBody": true,
"name": "constructor",
"params": [
{
"accessibility": "public",
"kind": "identifier",
"name": "name",
"optional": false,
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string",
},
"readonly": true,
},
{
"accessibility": "private",
"kind": "identifier",
"name": "private",
"optional": false,
"tsType": {
"repr": "number",
"kind": "keyword",
"keyword": "number",
},
},
{
"accessibility": "public",
"isOverride": true,
"kind": "identifier",
"name": "public",
"optional": false,
"tsType": {
"repr": "boolean",
"kind": "keyword",
"keyword": "boolean",
}
}
],
"location": {
"filename": "file:///test.ts",
"line": 3,
"col": 2,
},
}],
"properties": [],
"indexSignatures": [],
"methods": [],
"extends": null,
"implements": [],
"typeParams": [],
"superTypeParams": [],
}
}]);
json_test!(export_default_class,
r#"
/** Class doc */
export default class Foobar {
/** Constructor js doc */
constructor(name: string, private private2: number, protected protected2: number) {}
}
"#;
[{
"kind": "class",
"name": "default",
"location": {
"filename": "file:///test.ts",
"line": 3,
"col": 0
},
"declarationKind": "export",
"jsDoc": {
"doc": "Class doc",
},
"classDef": {
"isAbstract": false,
"extends": null,
"implements": [],
"typeParams": [],
"superTypeParams": [],
"constructors": [
{
"jsDoc": {
"doc": "Constructor js doc",
},
"accessibility": null,
"hasBody": true,
"name": "constructor",
"params": [
{
"name": "name",
"kind": "identifier",
"optional": false,
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
}
},
{
"accessibility": "private",
"name": "private2",
"kind": "identifier",
"optional": false,
"tsType": {
"repr": "number",
"kind": "keyword",
"keyword": "number"
}
},
{
"accessibility": "protected",
"name": "protected2",
"kind": "identifier",
"optional": false,
"tsType": {
"repr": "number",
"kind": "keyword",
"keyword": "number"
}
}
],
"location": {
"filename": "file:///test.ts",
"line": 5,
"col": 4
}
}
],
"properties": [],
"indexSignatures": [],
"methods": []
}
}]);
json_test!(export_default_fn,
r#"
export default function foo(a: number) {
return a;
}
"#;
[{
"kind": "function",
"name": "default",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 0
},
"declarationKind": "export",
"functionDef": {
"params": [
{
"kind": "identifier",
"name": "a",
"optional": false,
"tsType": {
"repr": "number",
"kind": "keyword",
"keyword": "number",
},
}
],
"returnType": null,
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": []
}
}]);
json_test!(export_default_interface,
r#"
/**
* Interface js doc
*/
export default interface Reader {
/** Read n bytes */
read?(buf: Uint8Array, something: unknown): Promise<number>
}
"#;
[{
"kind": "interface",
"name": "default",
"location": {
"filename": "file:///test.ts",
"line": 5,
"col": 0
},
"declarationKind": "export",
"jsDoc": {
"doc": "Interface js doc",
},
"interfaceDef": {
"extends": [],
"methods": [
{
"name": "read",
"kind": "method",
"location": {
"filename": "file:///test.ts",
"line": 7,
"col": 4
},
"optional": true,
"jsDoc": {
"doc": "Read n bytes",
},
"params": [
{
"name": "buf",
"kind": "identifier",
"optional": false,
"tsType": {
"repr": "Uint8Array",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "Uint8Array"
}
}
},
{
"name": "something",
"kind": "identifier",
"optional": false,
"tsType": {
"repr": "unknown",
"kind": "keyword",
"keyword": "unknown"
}
}
],
"typeParams": [],
"returnType": {
"repr": "Promise",
"kind": "typeRef",
"typeRef": {
"typeParams": [
{
"repr": "number",
"kind": "keyword",
"keyword": "number"
}
],
"typeName": "Promise"
}
}
}
],
"properties": [],
"callSignatures": [],
"indexSignatures": [],
"typeParams": []
}
}]);
json_test!(export_default_expr,
r#"export default "foo";"#;
[
{
"kind": "variable",
"name": "default",
"location": {
"filename": "file:///test.ts",
"line": 1,
"col": 0
},
"declarationKind": "export",
"variableDef": {
"tsType": {
"repr": "foo",
"kind": "literal",
"literal": {
"kind": "string",
"string": "foo"
}
},
"kind": "var"
}
}
]
);
json_test!(export_enum,
r#"
/**
* Some enum for good measure
*/
export enum Hello {
World = "world",
/** There is a JsDoc */
Fizz = "fizz",
Buzz = "buzz",
}
"#;
[{
"kind": "enum",
"name": "Hello",
"location": {
"filename": "file:///test.ts",
"line": 5,
"col": 0
},
"declarationKind": "export",
"jsDoc": {
"doc": "Some enum for good measure",
},
"enumDef": {
"members": [
{
"name": "World",
"init": {
"repr": "world",
"kind": "literal",
"literal": {
"kind": "string",
"string": "world",
}
},
"location": {
"filename": "file:///test.ts",
"line": 6,
"col": 4,
}
},
{
"name": "Fizz",
"init": {
"repr": "fizz",
"kind": "literal",
"literal": {
"kind": "string",
"string": "fizz",
}
},
"jsDoc": {
"doc": "There is a JsDoc"
},
"location": {
"filename": "file:///test.ts",
"line": 8,
"col": 4,
}
},
{
"name": "Buzz",
"init": {
"repr": "buzz",
"kind": "literal",
"literal": {
"kind": "string",
"string": "buzz",
}
},
"location": {
"filename": "file:///test.ts",
"line": 9,
"col": 4,
}
}
]
}
}]);
json_test!(export_fn,
r#"/**
* @module foo
*/
/**
* Hello there, this is a multiline JSdoc.
*
* It has many lines
*
* Or not that many?
*/
export function foo(a: string, b?: number, cb: (...cbArgs: unknown[]) => void, ...args: unknown[]): void {
/**
* @todo document all the things.
*/
console.log("Hello world");
}
"#;
[
{
"kind": "moduleDoc",
"name": "",
"location": {
"filename": "file:///test.ts",
"line": 1,
"col": 0,
},
"declarationKind": "export",
"jsDoc": {
"tags": [
{
"kind": "module"
}
]
},
}, {
"functionDef": {
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": [],
"params": [
{
"name": "a",
"kind": "identifier",
"optional": false,
"tsType": {
"keyword": "string",
"kind": "keyword",
"repr": "string",
},
},
{
"name": "b",
"kind": "identifier",
"optional": true,
"tsType": {
"keyword": "number",
"kind": "keyword",
"repr": "number",
},
},
{
"name": "cb",
"kind": "identifier",
"optional": false,
"tsType": {
"repr": "",
"kind": "fnOrConstructor",
"fnOrConstructor": {
"constructor": false,
"tsType": {
"keyword": "void",
"kind": "keyword",
"repr": "void"
},
"typeParams": [],
"params": [{
"arg": {
"name": "cbArgs",
"kind": "identifier",
"optional": false,
"tsType": null
},
"kind": "rest",
"tsType": {
"repr": "",
"kind": "array",
"array": {
"repr": "unknown",
"kind": "keyword",
"keyword": "unknown"
}
},
}]
}
},
},
{
"arg": {
"name": "args",
"kind": "identifier",
"optional": false,
"tsType": null
},
"kind": "rest",
"tsType": {
"array": {
"keyword": "unknown",
"kind": "keyword",
"repr": "unknown"
},
"kind": "array",
"repr": ""
}
}
],
"returnType": {
"keyword": "void",
"kind": "keyword",
"repr": "void",
},
},
"declarationKind": "export",
"jsDoc": {
"doc": "Hello there, this is a multiline JSdoc.\n\nIt has many lines\n\nOr not that many?",
},
"kind": "function",
"location": {
"col": 0,
"filename": "file:///test.ts",
"line": 12,
},
"name": "foo",
}]);
json_test!(export_fn2,
r#"
interface AssignOpts {
a: string;
b: number;
}
export function foo([e,,f, ...g]: number[], { c, d: asdf, i = "asdf", ...rest}, ops: AssignOpts = {}): void {
console.log("Hello world");
}
"#;
[{
"functionDef": {
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": [],
"params": [
{
"elements": [
{
"name": "e",
"kind": "identifier",
"optional": false,
"tsType": null
},
null,
{
"name": "f",
"kind": "identifier",
"optional": false,
"tsType": null
},
{
"arg": {
"name": "g",
"kind": "identifier",
"optional": false,
"tsType": null
},
"kind": "rest",
"tsType": null
}
],
"kind": "array",
"optional": false,
"tsType": {
"repr": "",
"kind": "array",
"array": {
"repr": "number",
"kind": "keyword",
"keyword": "number"
}
}
},
{
"kind": "object",
"optional": false,
"props": [
{
"kind": "assign",
"key": "c",
"value": null
},
{
"kind": "keyValue",
"key": "d",
"value": {
"name": "asdf",
"kind": "identifier",
"optional": false,
"tsType": null
}
},
{
"kind": "assign",
"key": "i",
"value": "[UNSUPPORTED]"
},
{
"arg": {
"name": "rest",
"kind": "identifier",
"optional": false,
"tsType": null
},
"kind": "rest"
}
],
"tsType": null
},
{
"kind": "assign",
"left": {
"name": "ops",
"kind": "identifier",
"optional": false,
"tsType": {
"repr": "AssignOpts",
"kind": "typeRef",
"typeRef": {
"typeName": "AssignOpts",
"typeParams": null,
}
}
},
"right": "[UNSUPPORTED]",
"tsType": null
}
],
"returnType": {
"keyword": "void",
"kind": "keyword",
"repr": "void",
},
},
"kind": "function",
"declarationKind": "export",
"location": {
"col": 0,
"filename": "file:///test.ts",
"line": 7,
},
"name": "foo",
}]);
json_test!(export_interface,
r#"
interface Foo {
foo(): void;
}
interface Bar {
bar(): void;
}
/**
* Interface js doc
*/
export interface Reader extends Foo, Bar {
/** Read n bytes */
read?(buf: Uint8Array, something: unknown): Promise<number>
}
"#;
[{
"kind": "interface",
"name": "Reader",
"location": {
"filename": "file:///test.ts",
"line": 11,
"col": 0
},
"declarationKind": "export",
"jsDoc": {
"doc": "Interface js doc",
},
"interfaceDef": {
"extends": [
{
"repr": "Foo",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "Foo"
}
},
{
"repr": "Bar",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "Bar"
}
}
],
"methods": [
{
"name": "read",
"kind": "method",
"location": {
"filename": "file:///test.ts",
"line": 13,
"col": 4
},
"optional": true,
"jsDoc": {
"doc": "Read n bytes",
},
"params": [
{
"name": "buf",
"kind": "identifier",
"optional": false,
"tsType": {
"repr": "Uint8Array",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "Uint8Array"
}
}
},
{
"name": "something",
"kind": "identifier",
"optional": false,
"tsType": {
"repr": "unknown",
"kind": "keyword",
"keyword": "unknown"
}
}
],
"typeParams": [],
"returnType": {
"repr": "Promise",
"kind": "typeRef",
"typeRef": {
"typeParams": [
{
"repr": "number",
"kind": "keyword",
"keyword": "number"
}
],
"typeName": "Promise"
}
}
}
],
"properties": [],
"callSignatures": [],
"indexSignatures": [],
"typeParams": [],
}
}]);
json_test!(export_interface2,
r#"
export interface TypedIface<T> {
something(): T
}
"#;
[{
"kind": "interface",
"name": "TypedIface",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 0
},
"declarationKind": "export",
"interfaceDef": {
"extends": [],
"methods": [
{
"name": "something",
"kind": "method",
"location": {
"filename": "file:///test.ts",
"line": 3,
"col": 4
},
"optional": false,
"params": [],
"typeParams": [],
"returnType": {
"repr": "T",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "T"
}
}
}
],
"properties": [],
"callSignatures": [],
"indexSignatures": [],
"typeParams": [
{ "name": "T" }
],
}
}]);
json_test!(export_interface_accessors,
r#"
export interface Thing {
get size(): number;
set size(value: number | string);
}
"#;
[{
"kind": "interface",
"name": "Thing",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 0
},
"declarationKind": "export",
"interfaceDef": {
"extends": [],
"methods": [
{
"name": "size",
"kind": "getter",
"location": {
"filename": "file:///test.ts",
"line": 3,
"col": 2,
},
"optional": false,
"params": [],
"typeParams": [],
"returnType": {
"repr": "number",
"kind": "keyword",
"keyword": "number",
},
},
{
"name": "size",
"kind": "setter",
"location": {
"filename": "file:///test.ts",
"line": 4,
"col": 2,
},
"optional": false,
"params": [
{
"kind": "identifier",
"name": "value",
"optional": false,
"tsType": {
"repr": "",
"kind": "union",
"union": [
{
"repr": "number",
"kind": "keyword",
"keyword": "number",
},
{
"repr": "string",
"kind": "keyword",
"keyword": "string",
}
]
}
}
],
"typeParams": [],
"returnType": null,
},
],
"properties": [],
"callSignatures": [],
"indexSignatures": [],
"typeParams": [],
}
}]
);
json_test!(export_type_alias,
r#"
/** Array holding numbers */
export type NumberArray = Array<number>;
"#;
[{
"kind": "typeAlias",
"name": "NumberArray",
"location": {
"filename": "file:///test.ts",
"line": 3,
"col": 0
},
"declarationKind": "export",
"jsDoc": {
"doc": "Array holding numbers",
},
"typeAliasDef": {
"typeParams": [],
"tsType": {
"repr": "Array",
"kind": "typeRef",
"typeRef": {
"typeParams": [
{
"repr": "number",
"kind": "keyword",
"keyword": "number"
}
],
"typeName": "Array"
}
}
}
}]);
json_test!(export_type_alias_literal,
r#"
export type A = {
new (d: string): A;
a(): void;
b?(): void;
get c(): string;
set c(v: number);
};
"#;
[{
"kind": "typeAlias",
"name": "A",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 0,
},
"declarationKind": "export",
"typeAliasDef": {
"typeParams": [],
"tsType": {
"repr": "",
"kind": "typeLiteral",
"typeLiteral": {
"methods": [
{
"name": "new",
"kind": "method",
"params": [
{
"kind": "identifier",
"name": "d",
"optional": false,
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string",
}
}
],
"optional": false,
"returnType": {
"repr": "A",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "A",
}
},
"typeParams": [],
},
{
"name": "a",
"kind": "method",
"params": [],
"optional": false,
"returnType": {
"repr": "void",
"kind": "keyword",
"keyword": "void",
},
"typeParams": [],
},
{
"name": "b",
"kind": "method",
"params": [],
"optional": true,
"returnType": {
"repr": "void",
"kind": "keyword",
"keyword": "void",
},
"typeParams": [],
},
{
"name": "c",
"kind": "getter",
"params": [],
"optional": false,
"returnType": {
"repr": "string",
"kind": "keyword",
"keyword": "string",
},
"typeParams": [],
},
{
"name": "c",
"kind": "setter",
"params": [
{
"kind": "identifier",
"name": "v",
"optional": false,
"tsType": {
"repr": "number",
"kind": "keyword",
"keyword": "number",
}
}
],
"optional": false,
"returnType": null,
"typeParams": [],
}
],
"properties": [],
"callSignatures": [],
"indexSignatures": []
}
}
}
}]);
json_test!(export_namespace,
r#"
/** Namespace JSdoc */
export namespace RootNs {
export const a = "a";
/** Nested namespace JSDoc */
export namespace NestedNs {
export enum Foo {
a = 1,
b = 2,
c = 3,
}
}
}
"#;
[{
"kind": "namespace",
"name": "RootNs",
"location": {
"filename": "file:///test.ts",
"line": 3,
"col": 0
},
"declarationKind": "export",
"jsDoc": {
"doc": "Namespace JSdoc",
},
"namespaceDef": {
"elements": [
{
"kind": "variable",
"name": "a",
"location": {
"filename": "file:///test.ts",
"line": 4,
"col": 17
},
"declarationKind": "export",
"variableDef": {
"tsType": {
"repr": "a",
"kind": "literal",
"literal": {
"kind": "string",
"string": "a"
}
},
"kind": "const"
}
},
{
"kind": "namespace",
"name": "NestedNs",
"location": {
"filename": "file:///test.ts",
"line": 7,
"col": 4
},
"declarationKind": "export",
"jsDoc": {
"doc": "Nested namespace JSDoc",
},
"namespaceDef": {
"elements": [
{
"kind": "enum",
"name": "Foo",
"location": {
"filename": "file:///test.ts",
"line": 8,
"col": 6
},
"declarationKind": "export",
"enumDef": {
"members": [
{
"name": "a",
"init": {
"repr": "1",
"kind": "literal",
"literal": {
"kind": "number",
"number": 1.0,
}
},
"location": {
"filename": "file:///test.ts",
"line": 9,
"col": 8,
}
},
{
"name": "b",
"init": {
"repr": "2",
"kind": "literal",
"literal": {
"kind": "number",
"number": 2.0,
}
},
"location": {
"filename": "file:///test.ts",
"line": 10,
"col": 8,
}
},
{
"name": "c",
"init": {
"repr": "3",
"kind": "literal",
"literal": {
"kind": "number",
"number": 3.0,
}
},
"location": {
"filename": "file:///test.ts",
"line": 11,
"col": 8,
}
}
]
}
}
]
}
}
]
}
}]);
json_test!(exports_declared_earlier,
r#"
const hello = "world";
function say(words: string): void { }
function foo(): void { }
export { hello, say, foo as bar };
"#;
[
{
"kind": "variable",
"name": "hello",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 0
},
"declarationKind": "export",
"variableDef": {
"tsType": {
"repr": "world",
"kind": "literal",
"literal": {
"kind": "string",
"string": "world"
}
},
"kind": "const"
}
},
{
"kind": "function",
"name": "say",
"location": {
"filename": "file:///test.ts",
"line": 3,
"col": 0
},
"declarationKind": "export",
"functionDef": {
"params": [
{
"kind": "identifier",
"name": "words",
"optional": false,
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
}
}
],
"returnType": {
"repr": "void",
"kind": "keyword",
"keyword": "void"
},
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": []
}
},
{
"kind": "function",
"name": "bar",
"location": {
"filename": "file:///test.ts",
"line": 4,
"col": 0
},
"declarationKind": "export",
"functionDef": {
"params": [],
"returnType": {
"repr": "void",
"kind": "keyword",
"keyword": "void"
},
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": []
}
}
]
);
json_test!(non_implemented_renamed_exports_declared_earlier,
r#"
declare function foo(): void;
export { foo as bar };
"#;
[
{
"kind": "function",
"name": "bar",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 2
},
"declarationKind": "export",
"functionDef": {
"params": [],
"returnType": {
"repr": "void",
"kind": "keyword",
"keyword": "void"
},
"isAsync": false,
"isGenerator": false,
"typeParams": []
}
}
]
);
json_test!(no_ambient_in_module,
r#"
declare function foo(): number;
export function bar() {};
"#;
[
{
"kind": "function",
"name": "bar",
"location": {
"filename": "file:///test.ts",
"line": 3,
"col": 0
},
"declarationKind": "export",
"functionDef": {
"params": [],
"returnType": null,
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": []
}
}
]
);
json_test!(default_exports_declared_earlier,
r#"
function foo(): void {}
export default foo;
"#;
[
{
"kind": "function",
"name": "default",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 0
},
"declarationKind": "export",
"functionDef": {
"params": [],
"returnType": {
"repr": "void",
"kind": "keyword",
"keyword": "void"
},
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": []
}
}
]
);
json_test!(reexport_existing_symbol,
r#"
export function foo(): void {}
export { foo as bar };
"#;
[
{
"kind": "function",
"name": "foo",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 0
},
"declarationKind": "export",
"functionDef": {
"params": [],
"returnType": {
"repr": "void",
"kind": "keyword",
"keyword": "void"
},
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": []
}
},
{
"kind": "function",
"name": "bar",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 0
},
"declarationKind": "export",
"functionDef": {
"params": [],
"returnType": {
"repr": "void",
"kind": "keyword",
"keyword": "void"
},
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": []
}
}
]
);
json_test!(optional_return_type,
r#"
export function foo(a: number) {
return a;
}
"#;
[{
"kind": "function",
"name": "foo",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 2
},
"declarationKind": "export",
"functionDef": {
"params": [
{
"name": "a",
"kind": "identifier",
"optional": false,
"tsType": {
"keyword": "number",
"kind": "keyword",
"repr": "number",
},
}
],
"typeParams": [],
"returnType": null,
"hasBody": true,
"isAsync": false,
"isGenerator": false
}
}]
);
json_test!(ts_lit_types,
r#"
export type boolLit = false;
export type strLit = "text";
export type tplLit = `text`;
export type tplLitArg = `test${number}`;
export type numLit = 5;
"#;
[
{
"kind": "typeAlias",
"name": "boolLit",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 0
},
"declarationKind": "export",
"typeAliasDef": {
"tsType": {
"repr": "false",
"kind": "literal",
"literal": {
"kind": "boolean",
"boolean": false
}
},
"typeParams": []
}
}, {
"kind": "typeAlias",
"name": "strLit",
"location": {
"filename": "file:///test.ts",
"line": 3,
"col": 0
},
"declarationKind": "export",
"typeAliasDef": {
"tsType": {
"repr": "text",
"kind": "literal",
"literal": {
"kind": "string",
"string": "text"
}
},
"typeParams": []
}
}, {
"kind": "typeAlias",
"name": "tplLit",
"location": {
"filename": "file:///test.ts",
"line": 4,
"col": 0
},
"declarationKind": "export",
"typeAliasDef": {
"tsType": {
"repr": "text",
"kind": "literal",
"literal": {
"kind": "template",
"tsTypes": [
{
"repr": "text",
"kind": "literal",
"literal": {
"kind": "string",
"string": "text"
}
}
]
}
},
"typeParams": []
}
}, {
"kind": "typeAlias",
"name": "tplLitArg",
"location": {
"filename": "file:///test.ts",
"line": 5,
"col": 0,
},
"declarationKind": "export",
"typeAliasDef": {
"tsType": {
"repr": "test${number}",
"kind": "literal",
"literal": {
"kind": "template",
"tsTypes": [
{
"repr": "test",
"kind": "literal",
"literal": {
"kind": "string",
"string": "test",
}
}, {
"repr": "number",
"kind": "keyword",
"keyword": "number"
}, {
"repr": "",
"kind": "literal",
"literal": {
"kind": "string",
"string": ""
}
}
]
}
},
"typeParams": [],
}
}, {
"kind": "typeAlias",
"name": "numLit",
"location": {
"filename": "file:///test.ts",
"line": 6,
"col": 0
},
"declarationKind": "export",
"typeAliasDef": {
"tsType": {
"repr": "5",
"kind": "literal",
"literal": {
"kind": "number",
"number": 5.0
}
},
"typeParams": []
}
}
]);
json_test!(export_private,
r#"
const foo: string = "foo";
export { foo };
"#,
private;
[
{
"kind": "variable",
"name": "foo",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 0
},
"declarationKind": "private",
"variableDef": {
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
},
"kind": "const"
}
},
{
"kind": "variable",
"name": "foo",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 0
},
"declarationKind": "export",
"variableDef": {
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
},
"kind": "const"
}
}
]
);
json_test!(ts_type_predicate_1,
r#"
export function foo(bar: A | B): bar is A {}
"#;
[
{
"kind": "function",
"name": "foo",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 0
},
"declarationKind": "export",
"functionDef": {
"params": [
{
"kind": "identifier",
"name": "bar",
"optional": false,
"tsType": {
"repr": "",
"kind": "union",
"union": [
{
"repr": "A",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "A"
}
},
{
"repr": "B",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "B"
}
}
]
}
}
],
"returnType": {
"repr": "bar is A",
"kind": "typePredicate",
"typePredicate": {
"asserts": false,
"param": {
"type": "identifier",
"name": "bar"
},
"type": {
"repr": "A",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "A"
}
}
}
},
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": []
}
}
]
);
json_test!(ts_type_predicate_2,
r#"
export function foo(bar: A | B): asserts bar is B {}
"#;
[
{
"kind": "function",
"name": "foo",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 0
},
"declarationKind": "export",
"functionDef": {
"params": [
{
"kind": "identifier",
"name": "bar",
"optional": false,
"tsType": {
"repr": "",
"kind": "union",
"union": [
{
"repr": "A",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "A"
}
},
{
"repr": "B",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "B"
}
}
]
}
}
],
"returnType": {
"repr": "asserts bar is B",
"kind": "typePredicate",
"typePredicate": {
"asserts": true,
"param": {
"type": "identifier",
"name": "bar"
},
"type": {
"repr": "B",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "B"
}
}
}
},
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": []
}
}
]
);
json_test!(ts_type_predicate_3,
r#"
export class C {
isSomething(): this is Something {}
}
"#;
[
{
"kind": "class",
"name": "C",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 0
},
"declarationKind": "export",
"classDef": {
"isAbstract": false,
"constructors": [],
"properties": [],
"indexSignatures": [],
"methods": [
{
"accessibility": null,
"optional": false,
"isAbstract": false,
"isStatic": false,
"name": "isSomething",
"kind": "method",
"functionDef": {
"params": [],
"returnType": {
"repr": "this is Something",
"kind": "typePredicate",
"typePredicate": {
"asserts": false,
"param": {
"type": "this"
},
"type": {
"repr": "Something",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "Something",
},
},
},
},
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": [],
},
"location": {
"filename": "file:///test.ts",
"line": 3,
"col": 2
}
}
],
"extends": null,
"implements": [],
"typeParams": [],
"superTypeParams": []
}
}
]
);
json_test!(ts_type_assertion,
r#"
export function foo(bar: any): asserts bar {}
"#;
[
{
"kind": "function",
"name": "foo",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 0
},
"declarationKind": "export",
"functionDef": {
"params": [
{
"kind": "identifier",
"name": "bar",
"optional": false,
"tsType": {
"repr": "any",
"kind": "keyword",
"keyword": "any"
}
}
],
"returnType": {
"repr": "asserts bar",
"kind": "typePredicate",
"typePredicate": {
"asserts": true,
"param": {
"type": "identifier",
"name": "bar"
},
"type": null,
}
},
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": []
}
}
]
);
json_test!(infer_types,
r#"export type Flatten<T> = T extends Array<infer U> ? U : T;"#;
[
{
"kind": "typeAlias",
"name": "Flatten",
"location": {
"filename": "file:///test.ts",
"line": 1,
"col": 0
},
"declarationKind": "export",
"typeAliasDef": {
"tsType": {
"repr": "",
"kind": "conditional",
"conditionalType": {
"checkType": {
"repr": "T",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "T"
}
},
"extendsType": {
"repr": "Array",
"kind": "typeRef",
"typeRef": {
"typeParams": [
{
"repr": "",
"kind": "infer",
"infer": {
"typeParam": {
"name": "U"
}
}
}
],
"typeName": "Array"
}
},
"trueType": {
"repr": "U",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "U"
}
},
"falseType": {
"repr": "T",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "T",
}
}
}
},
"typeParams": [
{
"name": "T"
}
]
}
}
]);
json_test!(infer_object_literal,
r#"
const s: symbol = Symbol.for("s");
const t: symbol = Symbol.for("t");
export const a = {
a: "a",
b: new Map<string, number>(),
c: { d: "d" },
d(e: string): void {},
f: (g: string): void => {},
get h(): string {
return "h";
},
set h(value: string) {
},
[s]: [1, 2, 3, "a"],
[t](u: string): void {},
};"#;
[
{
"kind": "variable",
"name": "a",
"location": {
"filename": "file:///test.ts",
"line": 5,
"col": 17
},
"declarationKind": "export",
"variableDef": {
"tsType": {
"repr": "",
"kind": "typeLiteral",
"typeLiteral": {
"methods": [
{
"name": "d",
"kind": "method",
"params": [
{
"kind": "identifier",
"name": "e",
"optional": false,
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
}
}
],
"optional": false,
"returnType": {
"repr": "void",
"kind": "keyword",
"keyword": "void"
},
"typeParams": []
},
{
"name": "h",
"kind": "getter",
"params": [],
"optional": false,
"returnType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
},
"typeParams": []
},
{
"name": "h",
"kind": "setter",
"params": [
{
"kind": "identifier",
"name": "value",
"optional": false,
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
}
}
],
"optional": false,
"returnType": null,
"typeParams": []
},
{
"name": "[t]",
"kind": "method",
"params": [
{
"kind": "identifier",
"name": "u",
"optional": false,
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
}
}
],
"computed": true,
"optional": false,
"returnType": {
"repr": "void",
"kind": "keyword",
"keyword": "void"
},
"typeParams": []
}
],
"properties": [
{
"name": "a",
"params": [],
"computed": false,
"optional": false,
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
},
"typeParams": []
},
{
"name": "b",
"params": [],
"computed": false,
"optional": false,
"tsType": {
"repr": "Map",
"kind": "typeRef",
"typeRef": {
"typeParams": [
{
"repr": "string",
"kind": "keyword",
"keyword": "string"
},
{
"repr": "number",
"kind": "keyword",
"keyword": "number"
}
],
"typeName": "Map"
}
},
"typeParams": []
},
{
"name": "c",
"params": [],
"computed": false,
"optional": false,
"tsType": {
"repr": "",
"kind": "typeLiteral",
"typeLiteral": {
"methods": [],
"properties": [
{
"name": "d",
"params": [],
"computed": false,
"optional": false,
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
},
"typeParams": []
}
],
"callSignatures": [],
"indexSignatures": []
}
},
"typeParams": []
},
{
"name": "f",
"params": [],
"computed": false,
"optional": false,
"tsType": {
"repr": "",
"kind": "fnOrConstructor",
"fnOrConstructor": {
"constructor": false,
"tsType": {
"repr": "void",
"kind": "keyword",
"keyword": "void"
},
"params": [
{
"kind": "identifier",
"name": "g",
"optional": false,
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
}
}
],
"typeParams": []
}
},
"typeParams": []
},
{
"name": "[s]",
"params": [],
"computed": true,
"optional": false,
"tsType": {
"repr": "",
"kind": "array",
"array": {
"repr": "",
"kind": "union",
"union": [
{
"repr": "number",
"kind": "keyword",
"keyword": "number"
},
{
"repr": "string",
"kind": "keyword",
"keyword": "string"
}
]
}
},
"typeParams": []
}
],
"callSignatures": [],
"indexSignatures": []
}
},
"kind": "const"
}
}
]
);
json_test!(mapped_types,
r#"
export type MappedTypeWithNewProperties<Type> = {
readonly [Properties in keyof Type as NewKeyType]?: Type[Properties];
};
"#;
[
{
"kind": "typeAlias",
"name": "MappedTypeWithNewProperties",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 0,
},
"declarationKind": "export",
"typeAliasDef": {
"tsType": {
"repr": "",
"kind": "mapped",
"mappedType": {
"readonly": true,
"typeParam": {
"name": "Properties",
"constraint": {
"repr": "",
"kind": "typeOperator",
"typeOperator": {
"operator": "keyof",
"tsType": {
"repr": "Type",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "Type"
}
}
}
}
},
"nameType": {
"repr": "NewKeyType",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "NewKeyType"
}
},
"optional": true,
"tsType": {
"repr": "",
"kind": "indexedAccess",
"indexedAccess": {
"readonly": false,
"objType": {
"repr": "Type",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "Type",
}
},
"indexType": {
"repr": "Properties",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "Properties"
}
}
}
}
}
},
"typeParams": [
{
"name": "Type",
}
]
}
}
]);
json_test!(import_types,
r#"
export function adopt<T>(p: import("./module.ts").Pet<T>) {
console.log(`Adopting ${p.name}...`);
}
"#;
[
{
"kind": "function",
"name": "adopt",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 0,
},
"declarationKind": "export",
"functionDef": {
"params": [
{
"kind": "identifier",
"name": "p",
"optional": false,
"tsType": {
"repr": "",
"kind": "importType",
"importType": {
"specifier": "./module.ts",
"qualifier": "Pet",
"typeParams": [
{
"repr": "T",
"kind": "typeRef",
"typeRef": {
"typeParams": null,
"typeName": "T"
}
}
]
}
}
}
],
"returnType": null,
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": [
{
"name": "T"
}
]
}
}
]
);
json_test!(indented_with_tabs,
r#"
/**
* Line 1
*
* Line 2
*
* Indented
*/
export namespace Tabs {
/**
* Line 1
*
* Line 2
*
* Indented
*/
export interface Tabs{
/**
* Line 1
*
* Line 2
*
* Indented
*/
property: string;
}
}
"#;
[
{
"kind": "namespace",
"name": "Tabs",
"location": {
"filename": "file:///test.ts",
"line": 9,
"col": 0
},
"jsDoc": {
"doc": "Line 1\n\nLine 2\n\n\tIndented",
},
"declarationKind": "export",
"namespaceDef": {
"elements": [
{
"kind": "interface",
"name": "Tabs",
"location": {
"filename": "file:///test.ts",
"line": 17,
"col": 4
},
"jsDoc": {
"doc": "Line 1\n\nLine 2\n\n\tIndented",
},
"declarationKind": "export",
"interfaceDef": {
"extends": [],
"methods": [],
"properties": [
{
"name": "property",
"location": {
"filename": "file:///test.ts",
"line": 25,
"col": 8
},
"jsDoc": {
"doc": "Line 1\n\nLine 2\n\n\tIndented",
},
"params": [],
"computed": false,
"optional": false,
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string"
},
"typeParams": []
}
],
"callSignatures": [],
"indexSignatures": [],
"typeParams": []
}
}
]
}
}
]
);
json_test!(overloads,
r#"
export function a(b: string): string;
export function a(b: number): number;
export function a(b: string | number): string | number {
//
}
"#;
[{
"kind": "function",
"name": "a",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 0
},
"declarationKind": "export",
"functionDef": {
"params": [{
"kind": "identifier",
"name": "b",
"optional": false,
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string",
},
}],
"returnType": {
"repr": "string",
"kind": "keyword",
"keyword": "string",
},
"isAsync": false,
"isGenerator": false,
"typeParams": [],
}
}, {
"kind": "function",
"name": "a",
"location": {
"filename": "file:///test.ts",
"line": 3,
"col": 0
},
"declarationKind": "export",
"functionDef": {
"params": [{
"kind": "identifier",
"name": "b",
"optional": false,
"tsType": {
"repr": "number",
"kind": "keyword",
"keyword": "number",
},
}],
"returnType": {
"repr": "number",
"kind": "keyword",
"keyword": "number",
},
"isAsync": false,
"isGenerator": false,
"typeParams": [],
}
}, {
"kind": "function",
"name": "a",
"location": {
"filename": "file:///test.ts",
"line": 4,
"col": 0
},
"declarationKind": "export",
"functionDef": {
"params": [{
"kind": "identifier",
"name": "b",
"optional": false,
"tsType": {
"repr": "",
"kind": "union",
"union": [{
"repr": "string",
"kind": "keyword",
"keyword": "string",
}, {
"repr": "number",
"kind": "keyword",
"keyword": "number"
}]
}
}],
"returnType": {
"repr": "",
"kind": "union",
"union": [{
"repr": "string",
"kind": "keyword",
"keyword": "string"
}, {
"repr": "number",
"kind": "keyword",
"keyword": "number"
}]
},
"hasBody": true,
"isAsync": false,
"isGenerator": false,
"typeParams": [],
}
}]);
json_test!(ctor_overloads,
r#"
export class A {
constructor (a: string);
constructor (a: number);
constructor (a: string | number) {
//
}
}"#;
[{
"kind": "class",
"name": "A",
"location": {
"filename": "file:///test.ts",
"line": 2,
"col": 0,
},
"declarationKind": "export",
"classDef": {
"isAbstract": false,
"constructors": [{
"accessibility": null,
"name": "constructor",
"params": [{
"kind": "identifier",
"name": "a",
"optional": false,
"tsType": {
"repr": "string",
"kind": "keyword",
"keyword": "string",
}
}],
"location": {
"filename": "file:///test.ts",
"line": 3,
"col": 2,
}
}, {
"accessibility": null,
"name": "constructor",
"params": [{
"kind": "identifier",
"name": "a",
"optional": false,
"tsType": {
"repr": "number",
"kind": "keyword",
"keyword": "number",
}
}],
"location": {
"filename": "file:///test.ts",
"line": 4,
"col": 2,
}
}, {
"accessibility": null,
"hasBody": true,
"name": "constructor",
"params": [{
"kind": "identifier",
"name": "a",
"optional": false,
"tsType": {
"repr": "",
"kind": "union",
"union": [{
"repr": "string",
"kind": "keyword",
"keyword": "string",
}, {
"repr": "number",
"kind": "keyword",
"keyword": "number",
}]
}
}],
"location": {
"filename": "file:///test.ts",
"line": 5,
"col": 2,
}
}],
"properties": [],
"indexSignatures": [],
"methods": [],
"extends": null,
"implements": [],
"typeParams": [],
"superTypeParams": [],
}
}]);
}
mod printer {
use crate::*;
contains_test!(abstract_class,
"export abstract class Class {}";
"abstract class Class"
);
contains_test!(abstract_class_abstract_method,
r#"
export abstract class Class {
abstract method() {}
}
"#;
"abstract method()"
);
contains_test!(class_async_method,
r#"
export class Class {
async amethod(v) {}
}
"#;
"async amethod(v)"
);
contains_test!(class_constructor,
r#"
export class Class {
constructor(public a, readonly b) {}
}
"#;
"constructor(public a, readonly b)"
);
contains_test!(class_details,
r#"
export class C {
/** a doc */
a() {}
f: number;
}
"#;
"class C",
"a()",
"f: number"
);
contains_test!(class_details_all_with_private,
r#"
export class Class {
private pri() {}
protected pro() {}
public pub() {}
}
"#,
private;
"private pri()",
"protected pro()",
"pub()"
);
contains_test!(class_details_only_non_private_without_private,
r#"
export class Class {
private pri() {}
protected pro() {}
public pub() {}
}
"#;
"protected pro()",
"pub()"
);
contains_test!(class_declaration,
"export class Class {}";
"class Class"
);
contains_test!(class_extends,
"export class Class extends Object {}";
"class Class extends Object"
);
contains_test!(class_extends_implements,
"export class Class extends Object implements Iterator, Iterable {}";
"class Class extends Object implements Iterator, Iterable"
);
contains_test!(class_generic_extends_implements,
"export class Class<A, B> extends Map<A, B> implements Iterator<A>, Iterable<B> {}";
"class Class<A, B> extends Map<A, B> implements Iterator<A>, Iterable<B>"
);
contains_test!(class_getter_and_setter,
r#"
export class Class {
get a(): void {}
set b(_v: void) {}
}
"#;
"get a(): void",
"set b(_v: void)"
);
contains_test!(class_override_prop_method,
r#"
export class C extends B {
override a(): void {}
override b: string;
}
"#;
"override a(): void",
"override b: string"
);
contains_test!(class_index_signature,
r#"
export class C {
[key: string]: number;
}
"#;
"[key: string]: number"
);
contains_test!(class_implements,
"export class Class implements Iterator {}";
"class Class implements Iterator"
);
contains_test!(class_implements2,
"export class Class implements Iterator, Iterable {}";
"class Class implements Iterator, Iterable"
);
contains_test!(class_method,
r#"
export class Class {
method(v) {}
}
"#;
"method(v)"
);
contains_test!(class_property,
r#"
export class Class {
someproperty: bool;
optproperty: bigint;
}
"#;
"someproperty: bool",
"optproperty: bigint"
);
contains_test!(class_readonly_index_signature,
r#"
export class C {
readonly [key: string]: number;
}
"#;
"readonly [key: string]: number"
);
contains_test!(class_static_property,
r#"
export class Class {
static property = "";
}
"#;
"static property"
);
contains_test!(class_readonly_property,
r#"
export class Class {
readonly property = "";
}
"#;
"readonly property"
);
contains_test!(class_private_property,
r#"
export class Class {
private property = "";
}
"#,
private;
"private property"
);
contains_test!(class_decorators,
r#"
@sealed
export class A {
#x = "x";
@format("Hello, %s")
greeting: string;
@configurable(false)
get x() {
return this.#x;
}
@enumerable(false)
greet() {
return "hello";
}
}
"#;
"@sealed",
"@format(\"Hello, %s\")",
"@configurable(false)",
"@enumerable(false)"
);
contains_test!(class_method_overloads,
r#"export class A {
a(b: string): string;
a(b: number): number;
a(b: string | number): string | number {}
}"#;
"a(b: string): string",
"a(b: number): number";
"a(b: string | number): string | number"
);
contains_test!(const_declaration,
"export const Const = 0;";
"const Const"
);
contains_test!(enum_declaration,
"export enum Enum {}";
"enum Enum"
);
contains_test!(enum_member,
r#"
export enum Enum {
First,
/** There is a JsDoc */
Second,
}
"#;
"enum Enum",
"First",
"Second",
"There is a JsDoc"
);
contains_test!(exports_all_with_private,
r#"
export function a() {}
function b() {}
export class C {}
class D {}
export interface E {}
interface F {}
export namespace G {}
namespace H {}
"#,
private;
"function a()",
"class C",
"interface E",
"namespace G",
"function b()",
"class D",
"interface F",
"namespace H"
);
contains_test!(exports_declared_earlier,
r#"
const hello = "world";
function say(words: string): void { }
function foo(): void { }
export { hello, say, foo as bar };
"#;
"const hello",
"function say(words: string): void",
"function bar(): void"
);
contains_test!(function_async,
"export async function a() {}";
"async function a()"
);
contains_test!(function_array_deconstruction,
"export function f([a, b, ...c]) {}";
"function f([a, b, ...c])"
);
contains_test!(function_async_generator,
"export async function* ag() {}";
"async function* ag()"
);
contains_test!(function_declaration,
"export function fun() {}";
"function fun()"
);
contains_test!(function_generator,
"export function* g() {}";
"function* g()"
);
contains_test!(function_generic,
"export function add<T>(a: T, b: T) { return a + b; }";
"function add<T>(a: T, b: T)"
);
contains_test!(function_object_deconstruction,
"export function f({ a, b, ...c }) {}";
"function f({a, b, ...c})"
);
contains_test!(function_overloads,
r#"
export function a(b: string): string;
export function a(b: number): number;
export function a(b: string | number): string | number {}
"#;
"function a(b: string): string",
"function a(b: number): number";
"function a(b: string | number): string | number"
);
contains_test!(generic_instantiated_with_tuple_type,
r#"
interface Generic<T> {}
export function f(): Generic<[string, number]> { return {}; }
"#;
"Generic<[string, number]>"
);
contains_test!(type_literal_declaration,
"export type T = {}";
"{ }"
);
contains_test!(type_literal_index_signature,
"export type T = { [key: string]: number; }";
"[key: string]: number"
);
contains_test!(type_literal_readonly_index_signature,
"export type T = { readonly [key: string]: number; }";
"readonly [key: string]: number"
);
contains_test!(type_alias_infer_type,
"export type Flatten<T> = T extends Array<infer U> ? U : T;";
"T extends Array<infer U> ? U : T"
);
contains_test!(type_literal_mapped_type,
"export type T<Type> = { readonly [P in keyof Type as NewType]: Type[P]; }";
"readonly [P in keyof Type as NewType]: Type[P]"
);
contains_test!(type_import_type,
"export function adopt<T>(p: import(\"./module.ts\").Pet<T>) { }";
"import(\"./module.ts\").Pet<T>"
);
contains_test!(interface_declaration,
"export interface Interface {}";
"interface Interface"
);
contains_test!(interface_extends,
"export interface Interface extends Iterator {}";
"interface Interface extends Iterator"
);
contains_test!(interface_extends2,
"export interface Interface extends Iterator, Iterable {}";
"interface Interface extends Iterator, Iterable"
);
contains_test!(interface_generic,
"export interface Interface<T> {}";
"interface Interface<T>"
);
contains_test!(interface_generic_extends,
"export interface Interface<V> extends Iterable<V> {}";
"interface Interface<V> extends Iterable<V>"
);
contains_test!(interface_index_signature,
r#"
export interface Interface {
[index: number]: Interface;
}
"#;
"[index: number]: Interface"
);
contains_test!(interface_method,
r#"
export interface I {
m(a, b);
mo?(c);
[mc](d);
}
"#;
"m(a, b)",
"mo?(c)",
"[mc](d)"
);
contains_test!(interface_property,
r#"
export interface I {
p: string;
po?: number;
readonly pro: string;
[pc]: string;
}
"#;
"p: string",
"po?: number",
"readonly pro: string",
"[pc]: string"
);
contains_test!(interface_string_literal_property,
r#"
export interface I {
"p": string;
"po"?: number;
}
"#;
"p: string",
"po?: number"
);
contains_test!(interface_number_literal_property,
r#"
export interface I {
1: string;
2?: number;
}
"#;
"1: string",
"2?: number"
);
contains_test!(interface_readonly_index_signature,
r#"
export interface Interface {
readonly [index: number]: Interface;
}
"#;
"readonly [index: number]: Interface"
);
contains_test!(interface_construct,
r#"
export interface I {
new(name: string);
}
"#;
"new(name: string)"
);
contains_test!(jsdoc,
r#"
/**
* A is a class
*
* Nothing more
*/
export class A {}
/**
* B is an interface
*
* Should be
*/
export interface B {}
/**
* C is a function
*
* Summarised
*/
export function C() {}
"#;
"A is a class",
"B is an interface",
"C is a function",
"Nothing more",
"Should be",
"Summarised"
);
contains_test!(jsdoc_tags,
r#"
/**
* a is a function
*
* @param {string} b some doc
* @param [c=1] additional doc
* @param [d] more doc
* @returns {string} returning doc
*/
export function a(b, c, d) {}
"#;
"a is a function",
"@param {string} b",
"some doc",
"@param [1] c",
"additional doc",
"@param [?] d",
"more doc",
"@return {string}",
"returning doc"
);
contains_test!(namespace_declaration,
"export namespace Namespace {}";
"namespace Namespace"
);
contains_test!(namespace_details,
r#"
export namespace Namespace {
/**
* Doc comment 1
*
* Details 1
*/
export function a() {}
/**
* Doc comment 2
*
* Details 2
*/
export class B {}
}
"#;
"namespace Namespace",
"function a()",
"class B",
"Doc comment 1",
"Doc comment 2",
"Details 1",
"Details 2"
);
contains_test!(namespace_fn_overloads,
r#"
export namespace Namespace {
export function a(b: string): string;
export function a(b: number): number;
export function a(b: number | string): number | string {}
}
"#;
"function a(b: string): string",
"function a(b: number): number";
"function a(b: number | string): number | string"
);
contains_test!(type_alias,
"export type A = number";
"type A = number"
);
contains_test!(type_generic_alias,
"export type A<T> = T";
"type A<T> = T"
);
contains_test!(infer_ts_types,
r#"
export let s = "hello";
export let n = 123;
export let b = false;
export let bi = 100n;
export let re = /hello/;
export let tpl = `foobar`;
"#;
"let s: string",
"let n: number",
"let b: boolean",
"let bi: bigint",
"let re: RegExp",
"let tpl: string"
);
contains_test!(infer_simple_ts_types,
r#"
export const s = "hello";
export const n = 123;
export const b = false;
export const bi = 100n;
export const re = /hello/;
export const tpl = `foobar`;
export const d = new Date();
export const s2 = String("foo");
export const n2 = Number(100);
export const bi2 = BigInt(123);
export const sym = Symbol("hello");
export const m = new Map<string, string>();
export const fn1 = (a: string): void => {};
export const fn2 = function (a:string): void {};
export const s3 = "VGhpcyBpcyBhIHJlYWxseSBsb25nIHN0cmluZyB0byB0cnkgdG8gZmluZCBvdXQgaWYgd2UgY2FuIHNraXAgaW5mZXJyaW5nIGEgcmVhbGx5IGxhcmdlIHZhbHVlIGZvciBhIGNvbnN0IHN0cmluZyB2YXJpYWJsZSB3aXRob3V0IGEgdHlwZSBhbm5vdGF0aW9uLg==";
"#;
"const s: \"hello\"",
"const n: 123",
"const b: false",
"const bi: 100",
"const re: RegExp",
"const tpl: `foobar`",
"const d: Date",
"const s2: string",
"const n2: number",
"const bi2: bigint",
"const sym: symbol",
"const m: Map<string, string>",
"const fn1: (a: string) => void",
"const fn2: (a: string) => void",
"const s3: string"
);
contains_test!(infer_simple_ts_arr_types,
r#"
export const a = [1];
export const b = [true];
export const c = ["c"];
export const d = [2, "d"];
export const e = [1, 2, 3];
export const f = ["a", 1, e];
export const g = ["a", 1, true] as const;
"#;
"const a: number[]",
"const b: boolean[]",
"const c: string[]",
"const d: (number | string)[]",
"const e: number[]",
"const f: any[]",
"const g: (\"a\" | 1 | true)[]"
);
contains_test!(
ts_template_with_args,
r#"
export const tpl: `test${number}` = `test1`;
"#;
"const tpl: `test${number}`"
);
contains_test!(
ts_user_defined_type_guards,
r#"
export function f1(val1: A | B): val1 is A {}
export function f2(val2: any): asserts val2 is string {}
export function f3(val3: any): asserts val3 {}
export function assertIsDefined<T>(val4: T): asserts val4 is NonNullable<T> {
if (val === undefined || val === null) {
throw new AssertionError(
`Expected 'val' to be defined, but received ${val}`
);
}
}
export class C {
isSomething(): this is Something {
return this instanceof Something;
}
}
"#;
"val1 is A",
"asserts val2 is string",
"asserts val3",
"asserts val4 is NonNullable<T>",
"this is Something"
);
}