1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
//! Private field access and brand checking for nominal class typing.
use crate::state::CheckerState;
use tsz_solver::TypeId;
// =============================================================================
// Private Field Checking Methods
// =============================================================================
impl<'a> CheckerState<'a> {
// =========================================================================
// Private Brand Extraction
// =========================================================================
/// Get the private brand property from a type.
///
/// Private members in classes use a "brand" property for nominal typing.
/// This brand is a property named like `__private_brand_#className`.
///
/// Returns `Some(brand_name)` if the type has a private brand.
pub(crate) fn get_private_brand(&self, type_id: TypeId) -> Option<String> {
tsz_solver::type_queries::get_private_brand_name(self.ctx.types, type_id)
}
// =========================================================================
// Private Brand Comparison
// =========================================================================
/// Check if two types have the same private brand.
///
/// This is used for nominal typing of private member access. Private members
/// can only be accessed from instances of the same class that declared them.
///
/// Returns true if both types have the same private brand.
pub(crate) fn types_have_same_private_brand(&self, type1: TypeId, type2: TypeId) -> bool {
match (self.get_private_brand(type1), self.get_private_brand(type2)) {
(Some(brand1), Some(brand2)) => brand1 == brand2,
_ => false,
}
}
// =========================================================================
// Private Field Name Extraction
// =========================================================================
/// Extract the name of the private field from a brand string.
///
/// Given a type with a private brand, returns the actual private field name
/// (e.g., "#foo") if found.
///
/// Returns `Some(private_field_name)` if found, None otherwise.
pub(crate) fn get_private_field_name_from_brand(&self, type_id: TypeId) -> Option<String> {
tsz_solver::type_queries::get_private_field_name(self.ctx.types, type_id)
}
// =========================================================================
// Private Brand Mismatch Error
// =========================================================================
/// Check if there's a private brand mismatch between two types.
///
/// When accessing a private member, TypeScript checks that the object has the same
/// private brand as the class declaring the member. This function generates an
/// appropriate error message for mismatches.
///
/// Returns `Some(error_message)` if there's a private brand mismatch.
pub(crate) fn private_brand_mismatch_error(
&self,
source: TypeId,
target: TypeId,
) -> Option<String> {
let source_brand = self.get_private_brand(source)?;
let target_brand = self.get_private_brand(target)?;
if source_brand == target_brand {
return None;
}
let field_name = self
.get_private_field_name_from_brand(source)
.unwrap_or_else(|| "[private field]".to_string());
Some(format!(
"Property '{}' in type '{}' refers to a different member that cannot be accessed from within type '{}'.",
field_name,
self.format_type(source),
self.format_type(target)
))
}
}