use crate::checker::context::CheckerOptions;
use crate::checker::state::CheckerState;
use crate::test_fixtures::TestContext;
use std::sync::Arc;
use tsz_binder::BinderState;
use tsz_parser::parser::ParserState;
use tsz_solver::TypeInterner;
const GLOBAL_TYPE_MOCKS: &str = r#"
interface Array<T> {}
interface String {}
interface Boolean {}
interface Number {}
interface Object {}
interface Function {}
interface RegExp {}
interface IArguments {}
interface Promise<T> {}
"#;
fn test_no_errors(source: &str) {
let source = format!("// @strictFunctionTypes: true\n{GLOBAL_TYPE_MOCKS}\n{source}");
let ctx = TestContext::new();
let mut parser = ParserState::new("test.ts".to_string(), source);
let root = parser.parse_source_file();
let mut binder = BinderState::new();
binder.bind_source_file_with_libs(parser.get_arena(), root, &ctx.lib_files);
let types = TypeInterner::new();
let mut checker = CheckerState::new(
parser.get_arena(),
&binder,
&types,
"test.ts".to_string(),
CheckerOptions::default(),
);
if !ctx.lib_files.is_empty() {
let lib_contexts: Vec<crate::checker::context::LibContext> = ctx
.lib_files
.iter()
.map(|lib| crate::checker::context::LibContext {
arena: Arc::clone(&lib.arena),
binder: Arc::clone(&lib.binder),
})
.collect();
checker.ctx.set_lib_contexts(lib_contexts);
}
checker.check_source_file(root);
let errors: Vec<_> = checker
.ctx
.diagnostics
.iter()
.filter(|d| {
d.category == crate::checker::diagnostics::DiagnosticCategory::Error && d.code != 2318
})
.collect();
assert!(
errors.is_empty(),
"Expected no errors, got {}: {:?}",
errors.len(),
errors
);
}
fn test_expect_error(source: &str, expected_error_code: u32) {
let source = format!("// @strictFunctionTypes: true\n{GLOBAL_TYPE_MOCKS}\n{source}");
let ctx = TestContext::new();
let mut parser = ParserState::new("test.ts".to_string(), source);
let root = parser.parse_source_file();
let mut binder = BinderState::new();
binder.bind_source_file_with_libs(parser.get_arena(), root, &ctx.lib_files);
let types = TypeInterner::new();
let mut checker = CheckerState::new(
parser.get_arena(),
&binder,
&types,
"test.ts".to_string(),
CheckerOptions::default(),
);
if !ctx.lib_files.is_empty() {
let lib_contexts: Vec<crate::checker::context::LibContext> = ctx
.lib_files
.iter()
.map(|lib| crate::checker::context::LibContext {
arena: Arc::clone(&lib.arena),
binder: Arc::clone(&lib.binder),
})
.collect();
checker.ctx.set_lib_contexts(lib_contexts);
}
checker.check_source_file(root);
let error_count = checker
.ctx
.diagnostics
.iter()
.filter(|d| d.code == expected_error_code)
.count();
assert!(
error_count >= 1,
"Expected at least 1 TS{} error, got {}: {:?}",
expected_error_code,
error_count,
checker.ctx.diagnostics
);
}
#[test]
fn test_void_return_basic() {
test_no_errors(
r#"
function takesCallback(cb: () => void) {
cb();
}
takesCallback(() => "hello");
"#,
);
}
#[test]
fn test_void_return_number() {
test_no_errors(
r#"
function takesCallback(cb: () => void) {
cb();
}
takesCallback(() => 42);
"#,
);
}
#[test]
fn test_void_return_object() {
test_no_errors(
r#"
function takesCallback(cb: () => void) {
cb();
}
takesCallback(() => ({ x: 1 }));
"#,
);
}
#[test]
fn test_undefined_return_not_assignable_to_void() {
test_expect_error(
r#"
type Callback = () => undefined;
const f: Callback = () => "hello";
"#,
2322, );
}
#[test]
#[ignore = "Pre-existing failure: Promise resolves to TS2690 instead of TS2322"]
fn test_promise_void_strictness() {
test_expect_error(
r#"
type AsyncCallback = () => Promise<void>;
const f: AsyncCallback = () => Promise.resolve("hello");
"#,
2322, );
}
#[test]
fn test_void_return_interface_assignment() {
test_no_errors(
r#"
interface VoidCallback {
method(): void;
}
const impl: VoidCallback = {
method: () => "returns value but ignored"
};
"#,
);
}
#[test]
fn test_void_return_function_expression() {
test_no_errors(
r#"
function takesCallback(cb: () => void) {
cb();
}
takesCallback(function() { return "ignored"; });
"#,
);
}
#[test]
fn test_void_return_arrow_function() {
test_no_errors(
r#"
function takesCallback(cb: () => void) {
cb();
}
takesCallback(() => { return "ignored"; });
"#,
);
}
#[test]
fn test_void_not_assignable_to_string() {
test_expect_error(
r#"
type StringCallback = () => string;
const f: StringCallback = () => {};
"#,
2322, );
}
#[test]
fn test_void_return_array_callbacks() {
test_no_errors(
r#"
const callbacks: Array<() => void> = [
() => 1,
() => "hello",
() => ({ x: 1 })
];
"#,
);
}