pub struct NarrowingContext<'a> { /* private fields */ }Expand description
Narrowing context for type guards and control flow analysis.
Implementations§
Source§impl<'a> NarrowingContext<'a>
impl<'a> NarrowingContext<'a>
Sourcepub fn narrow_by_nullishness(
&self,
source_type: TypeId,
nullish: bool,
) -> TypeId
pub fn narrow_by_nullishness( &self, source_type: TypeId, nullish: bool, ) -> TypeId
Narrow a type by removing definitely falsy values (truthiness check).
Narrow a type to its falsy component(s).
This is used for the false branch of truthiness checks (e.g., if (!x)).
Returns the union of all falsy values that the type could be.
Falsy values in TypeScript:
- null, undefined, void
- false (boolean literal)
- 0, -0,
NaN(number literals) - “” (empty string)
- 0n (bigint literal)
CRITICAL: TypeScript does NOT narrow primitive types in falsy branches.
For boolean, number, string, and bigint, they stay as their primitive type.
For unknown, TypeScript does NOT narrow in falsy branches.
Only literal types are narrowed (e.g., 0 | 1 -> 0, true | false -> false).
Narrows a type by nullishness (like if (x != null) or if (x == null)).
If nullish is true, returns the nullish part (null | undefined).
If nullish is false, returns the non-nullish part.
pub fn narrow_to_falsy(&self, type_id: TypeId) -> TypeId
Sourcepub fn narrow_by_truthiness(&self, source_type: TypeId) -> TypeId
pub fn narrow_by_truthiness(&self, source_type: TypeId) -> TypeId
This matches TypeScript’s behavior where if (x) narrows out:
- null, undefined, void
- false (boolean literal)
- 0, -0,
NaN(number literals) - “” (empty string)
- 0n (bigint literal)
Sourcepub fn narrow(&self, type_id: TypeId, narrower: TypeId) -> TypeId
pub fn narrow(&self, type_id: TypeId, narrower: TypeId) -> TypeId
Narrows a type by another type using the Visitor pattern.
This is the general-purpose narrowing function that implements the Solver-First architecture (North Star Section 3.1). The Checker identifies WHERE narrowing happens (AST nodes) and the Solver calculates the RESULT.
§Arguments
type_id- The type to narrow (e.g., a union type)narrower- The type to narrow by (e.g., a literal type)
§Returns
The narrowed type. For unions, filters to members assignable to narrower. For type parameters, intersects with narrower.
§Examples
narrow("A" | "B", "A")→"A"narrow(string | number, "hello")→"hello"narrow(T | null, undefined)→null(filters out T)
Source§impl<'a> NarrowingContext<'a>
impl<'a> NarrowingContext<'a>
Sourcepub fn find_discriminants(&self, union_type: TypeId) -> Vec<DiscriminantInfo>
pub fn find_discriminants(&self, union_type: TypeId) -> Vec<DiscriminantInfo>
Find discriminant properties in a union type.
A discriminant property is one where:
- All union members have the property
- Each member has a unique literal type for that property
Sourcepub fn narrow_by_discriminant_for_type(
&self,
type_id: TypeId,
prop_path: &[Atom],
literal_type: TypeId,
is_true_branch: bool,
) -> TypeId
pub fn narrow_by_discriminant_for_type( &self, type_id: TypeId, prop_path: &[Atom], literal_type: TypeId, is_true_branch: bool, ) -> TypeId
Narrow a union type based on a discriminant property check.
Example: action.type === "add" narrows Action to { type: "add", value: number }
Uses a filtering approach: checks each union member individually to see if
the property could match the literal value. This is more flexible than the
old find_discriminants approach which required ALL members to have the
property with unique literal values.
§Arguments
Narrow a type by discriminant, handling type parameter constraints.
If the type is a type parameter with a constraint, narrows the constraint and intersects with the type parameter when the constraint is affected.
Sourcepub fn narrow_by_property_truthiness(
&self,
union_type: TypeId,
property_path: &[Atom],
sense: bool,
) -> TypeId
pub fn narrow_by_property_truthiness( &self, union_type: TypeId, property_path: &[Atom], sense: bool, ) -> TypeId
Narrows a union type based on whether a property is truthy or falsy.
This is used for conditionals like if (x.prop) when x is a union.
Sourcepub fn narrow_by_discriminant(
&self,
union_type: TypeId,
property_path: &[Atom],
literal_value: TypeId,
) -> TypeId
pub fn narrow_by_discriminant( &self, union_type: TypeId, property_path: &[Atom], literal_value: TypeId, ) -> TypeId
union_type: The union type to narrowproperty_path: Path to the discriminant property (e.g., [“payload”, “type”])literal_value: The literal value to match
Sourcepub fn narrow_by_excluding_discriminant(
&self,
union_type: TypeId,
property_path: &[Atom],
excluded_value: TypeId,
) -> TypeId
pub fn narrow_by_excluding_discriminant( &self, union_type: TypeId, property_path: &[Atom], excluded_value: TypeId, ) -> TypeId
Narrow a union type by excluding variants with a specific discriminant value.
Example: action.type !== "add" narrows to { type: "remove", ... } | { type: "clear" }
Uses the inverse logic of narrow_by_discriminant: we exclude a member
ONLY if its property is definitely and only the excluded value.
For example:
- prop is “a”, exclude “a” -> exclude (property is always “a”)
- prop is “a” | “b”, exclude “a” -> keep (could be “b”)
- prop doesn’t exist -> keep (property doesn’t match excluded value)
§Arguments
union_type: The union type to narrowproperty_path: Path to the discriminant property (e.g., [“payload”, “type”])excluded_value: The literal value to exclude
Sourcepub fn narrow_by_excluding_discriminant_values(
&self,
union_type: TypeId,
property_path: &[Atom],
excluded_values: &[TypeId],
) -> TypeId
pub fn narrow_by_excluding_discriminant_values( &self, union_type: TypeId, property_path: &[Atom], excluded_values: &[TypeId], ) -> TypeId
Narrow a union type by excluding variants with any of the specified discriminant values.
This is an optimized batch version of narrow_by_excluding_discriminant for switch statements.
Source§impl<'a> NarrowingContext<'a>
impl<'a> NarrowingContext<'a>
Sourcepub fn narrow_by_instanceof(
&self,
source_type: TypeId,
constructor_type: TypeId,
sense: bool,
) -> TypeId
pub fn narrow_by_instanceof( &self, source_type: TypeId, constructor_type: TypeId, sense: bool, ) -> TypeId
Narrow a type based on an instanceof check.
Example: x instanceof MyClass narrows A | B to include only A where A is an instance of MyClass
Sourcepub fn narrow_by_instance_type(
&self,
source_type: TypeId,
instance_type: TypeId,
) -> TypeId
pub fn narrow_by_instance_type( &self, source_type: TypeId, instance_type: TypeId, ) -> TypeId
Narrow a type by instanceof check using the instance type.
Unlike narrow_to_type which uses structural assignability to filter union members,
this method uses instanceof-specific semantics:
- Type parameters with constraints assignable to the target are kept (intersected)
- When a type parameter absorbs the target, anonymous object types are excluded since they cannot be class instances at runtime
This prevents anonymous object types like { x: string } from surviving instanceof
narrowing when they happen to be structurally compatible with the class type.
Sourcepub fn narrow_by_instanceof_false(
&self,
source_type: TypeId,
instance_type: TypeId,
) -> TypeId
pub fn narrow_by_instanceof_false( &self, source_type: TypeId, instance_type: TypeId, ) -> TypeId
Narrow a type for the false branch of instanceof.
Keeps primitive types (which can never pass instanceof) and excludes
non-primitive members that are subtypes of the instance type.
For example, string | number | Date with instanceof Object false
branch gives string | number (Date is excluded as it’s an Object instance).
Source§impl<'a> NarrowingContext<'a>
impl<'a> NarrowingContext<'a>
Source§impl<'a> NarrowingContext<'a>
impl<'a> NarrowingContext<'a>
pub fn new(db: &'a dyn QueryDatabase) -> Self
Sourcepub fn with_cache(db: &'a dyn QueryDatabase, cache: &'a NarrowingCache) -> Self
pub fn with_cache(db: &'a dyn QueryDatabase, cache: &'a NarrowingCache) -> Self
Create a new context with a shared cache.
Sourcepub fn with_resolver(self, resolver: &'a dyn TypeResolver) -> Self
pub fn with_resolver(self, resolver: &'a dyn TypeResolver) -> Self
Set the TypeResolver for this context.
This enables proper resolution of Lazy types (type aliases) during narrowing.
The resolver should be borrowed from the Checker’s TypeEnvironment.
Sourcepub fn narrow_by_typeof(
&self,
source_type: TypeId,
typeof_result: &str,
) -> TypeId
pub fn narrow_by_typeof( &self, source_type: TypeId, typeof_result: &str, ) -> TypeId
Narrow a type based on a typeof check.
Example: typeof x === "string" narrows string | number to string
Sourcepub fn narrow_to_type(&self, source_type: TypeId, target_type: TypeId) -> TypeId
pub fn narrow_to_type(&self, source_type: TypeId, target_type: TypeId) -> TypeId
Narrow a type to include only members assignable to target.
Sourcepub fn literal_assignable_to(&self, literal: TypeId, target: TypeId) -> bool
pub fn literal_assignable_to(&self, literal: TypeId, target: TypeId) -> bool
Check if a literal type is assignable to a target for narrowing purposes.
Handles union decomposition: if the target is a union, checks each member.
Falls back to narrow_to_type to determine if the literal can narrow to the target.
Sourcepub fn narrow_excluding_type(
&self,
source_type: TypeId,
excluded_type: TypeId,
) -> TypeId
pub fn narrow_excluding_type( &self, source_type: TypeId, excluded_type: TypeId, ) -> TypeId
Narrow a type to exclude members assignable to target.
Sourcepub fn narrow_excluding_types(
&self,
source_type: TypeId,
excluded_types: &[TypeId],
) -> TypeId
pub fn narrow_excluding_types( &self, source_type: TypeId, excluded_types: &[TypeId], ) -> TypeId
Narrow a type by excluding multiple types at once (batched version).
This is an optimized version of narrow_excluding_type for cases like
switch default clauses where we need to exclude many types at once.
It avoids creating intermediate union types and reduces complexity from O(N²) to O(N).
§Arguments
source_type- The type to narrow (typically a union)excluded_types- Types to exclude from the source
§Returns
The narrowed type with all excluded types removed
Sourcepub fn narrow_excluding_function(&self, source_type: TypeId) -> TypeId
pub fn narrow_excluding_function(&self, source_type: TypeId) -> TypeId
Narrow a type to exclude function-like members (typeof !== “function”).
Sourcepub fn narrow_type(
&self,
source_type: TypeId,
guard: &TypeGuard,
sense: bool,
) -> TypeId
pub fn narrow_type( &self, source_type: TypeId, guard: &TypeGuard, sense: bool, ) -> TypeId
Applies a type guard to narrow a type.
This is the main entry point for AST-agnostic type narrowing.
The Checker extracts a TypeGuard from AST nodes, and the Solver
applies it to compute the narrowed type.
§Arguments
source_type- The type to narrowguard- The guard condition (extracted from AST by Checker)sense- If true, narrow for the “true” branch; if false, narrow for the “false” branch
§Returns
The narrowed type after applying the guard.
§Examples
// typeof x === "string"
let guard = TypeGuard::Typeof(TypeofKind::String);
let narrowed = narrowing.narrow_type(string_or_number, &guard, true);
assert_eq!(narrowed, TypeId::STRING);
// x !== null (negated sense)
let guard = TypeGuard::NullishEquality;
let narrowed = narrowing.narrow_type(string_or_null, &guard, false);
// Result should exclude null and undefined