Skip to main content

NarrowingContext

Struct NarrowingContext 

Source
pub struct NarrowingContext<'a> { /* private fields */ }
Expand description

Narrowing context for type guards and control flow analysis.

Implementations§

Source§

impl<'a> NarrowingContext<'a>

Source

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.

Source

pub fn narrow_to_falsy(&self, type_id: TypeId) -> TypeId

Source

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)
Source

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>

Source

pub fn find_discriminants(&self, union_type: TypeId) -> Vec<DiscriminantInfo>

Find discriminant properties in a union type.

A discriminant property is one where:

  1. All union members have the property
  2. Each member has a unique literal type for that property
Source

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.

Source

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.

Source

pub fn narrow_by_discriminant( &self, union_type: TypeId, property_path: &[Atom], literal_value: TypeId, ) -> TypeId

  • union_type: The union type to narrow
  • property_path: Path to the discriminant property (e.g., [“payload”, “type”])
  • literal_value: The literal value to match
Source

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 narrow
  • property_path: Path to the discriminant property (e.g., [“payload”, “type”])
  • excluded_value: The literal value to exclude
Source

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>

Source

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

Source

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.

Source

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>

Source

pub fn narrow_by_property_presence( &self, source_type: TypeId, property_name: Atom, present: bool, ) -> TypeId

Narrow a type based on an in operator check.

Example: "a" in x narrows A | B to include only types that have property a

Source§

impl<'a> NarrowingContext<'a>

Source

pub fn new(db: &'a dyn QueryDatabase) -> Self

Source

pub fn with_cache(db: &'a dyn QueryDatabase, cache: &'a NarrowingCache) -> Self

Create a new context with a shared cache.

Source

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.

Source

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

Source

pub fn narrow_to_type(&self, source_type: TypeId, target_type: TypeId) -> TypeId

Narrow a type to include only members assignable to target.

Source

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.

Source

pub fn narrow_excluding_type( &self, source_type: TypeId, excluded_type: TypeId, ) -> TypeId

Narrow a type to exclude members assignable to target.

Source

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

Source

pub fn narrow_excluding_function(&self, source_type: TypeId) -> TypeId

Narrow a type to exclude function-like members (typeof !== “function”).

Source

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 narrow
  • guard - 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

Auto Trait Implementations§

§

impl<'a> !Freeze for NarrowingContext<'a>

§

impl<'a> !RefUnwindSafe for NarrowingContext<'a>

§

impl<'a> !Send for NarrowingContext<'a>

§

impl<'a> !Sync for NarrowingContext<'a>

§

impl<'a> Unpin for NarrowingContext<'a>

§

impl<'a> UnsafeUnpin for NarrowingContext<'a>

§

impl<'a> !UnwindSafe for NarrowingContext<'a>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<S, T> Upcast<T> for S
where T: UpcastFrom<S> + ?Sized, S: ?Sized,

Source§

fn upcast(&self) -> &T
where Self: ErasableGeneric, T: ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider ref type within the Wasm bindgen generics type system. Read more
Source§

fn upcast_into(self) -> T
where Self: Sized + ErasableGeneric, T: ErasableGeneric<Repr = Self::Repr>,

Perform a zero-cost type-safe upcast to a wider type within the Wasm bindgen generics type system. Read more
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more