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.code != 2318) .collect();
if !errors.is_empty() {
panic!(
"Expected no errors, but got:\n{}",
errors
.iter()
.map(|d| format!(" {}", d.message_text))
.collect::<Vec<_>>()
.join("\n")
);
}
}
#[test]
fn test_const_assertion_primitive_literal() {
test_no_errors(
r#"
const x = "hello" as const;
// x should have type "hello", not string
let y: "hello" = x; // Should be allowed
"#,
);
}
#[test]
fn test_const_assertion_number_literal() {
test_no_errors(
r#"
const x = 42 as const;
// x should have type 42, not number
let y: 42 = x; // Should be allowed
"#,
);
}
#[test]
fn test_const_assertion_boolean_literal() {
test_no_errors(
r#"
const x = true as const;
// x should have type true, not boolean
let y: true = x; // Should be allowed
"#,
);
}
#[test]
fn test_const_assertion_array_becomes_readonly_tuple() {
test_no_errors(
r#"
const arr = [1, 2, 3] as const;
// arr should be readonly tuple [1, 2, 3]
let first: 1 = arr[0]; // Should be allowed
let second: 2 = arr[1]; // Should be allowed
"#,
);
}
#[test]
fn test_const_assertion_object_properties_readonly() {
test_no_errors(
r#"
const obj = { x: 1, y: "hello" } as const;
// obj.x should be readonly with type 1
// obj.y should be readonly with type "hello"
const val1: 1 = obj.x; // Should be allowed
const val2: "hello" = obj.y; // Should be allowed
"#,
);
}
#[test]
fn test_const_assertion_nested_object() {
test_no_errors(
r#"
const obj = {
a: 1,
nested: {
b: "hello",
c: true
}
} as const;
// All properties should be readonly with literal types
const val1: 1 = obj.a; // Should be allowed
const val2: "hello" = obj.nested.b; // Should be allowed
const val3: true = obj.nested.c; // Should be allowed
"#,
);
}
#[test]
fn test_const_assertion_mixed_array_and_object() {
test_no_errors(
r#"
const obj = {
x: 1,
arr: [1, 2, 3]
} as const;
// obj.x should be readonly 1
// obj.arr should be readonly tuple [1, 2, 3]
const val: 1 = obj.x; // Should be allowed
"#,
);
}
#[test]
fn test_const_assertion_template_literal() {
test_no_errors(
r#"
const x = `hello` as const;
// x should have type `hello`, not string
const y: `hello` = x; // Should be allowed
"#,
);
}
#[test]
fn test_const_assertion_null_and_undefined() {
test_no_errors(
r#"
const x = null as const;
const y = undefined as const;
const a: null = x; // Should be allowed
const b: undefined = y; // Should be allowed
"#,
);
}
#[test]
fn test_const_assertion_nested_array() {
test_no_errors(
r#"
const arr = [[1, 2], [3, 4]] as const;
// arr should be readonly tuple of readonly tuples
const val: 1 = arr[0][0]; // Should be allowed
"#,
);
}
#[test]
fn test_const_assertion_array_of_objects() {
test_no_errors(
r#"
const arr = [{ x: 1 }, { y: 2 }] as const;
// Each object should have readonly properties
const val: 1 = arr[0].x; // Should be allowed
"#,
);
}