Imports

Imports make built-in modules, wrapper packages, and code from other Typical files available inside a space.

Typical intentionally has a small import syntax. Ordinary package imports use canonical names rather than strings or paths. Named, default, namespace, type-only, and dynamic TypeScript imports are not supported.

Name Categories

Typical has three relevant categories of names:

Standard built-ins are always available. Module built-ins and wrapper packages must be imported before use.

A wrapper package is a Typical-facing module generated around a Rust crate. Typical code imports the wrapper rather than accessing the crate directly. The wrapper adapts the crate's API, types, ownership behavior, documentation, startup metadata, and Cargo features to Typical.

Module Imports

The basic import form uses a module's canonical name:

import Postgres

An alias changes the local binding:

import Postgres as MyPostgres

The alias may be any valid non-reserved Typical identifier. UpperCamelCase is conventional but not mechanically required. An alias affects only lexical lookup; it does not rename the package or its API.

An import may specify Cargo features and an exact package version:

import Sqlx features postgres mysql sqlite version 1.2.3

Features are whitespace-separated. When an alias, features, and a version are all present, they occur in that order, with the version last:

import Sqlx as Database features postgres mysql sqlite version 1.2.3

Declared Import Policy

A declare import establishes inherited package policy without introducing a module binding:

declare import Sqlx features postgres mysql sqlite version 1.2.3

The declaration applies in its immediate space and child spaces. A matching import inherits any version or features it does not state itself:

declare import Sqlx features postgres version 1.2.3

space DataAccess (
	import Sqlx
)

Import-local options take precedence over the nearest visible declare import. A declaration does not initialize or include a package unless a matching import exists.

Version Resolution

Versions are exact semantic versions. A version written on an import or inherited from declare import selects that wrapper and its underlying crate version.

Bare imports are valid:

import Postgres

The package manager first selects the newest compatible wrapper version already available in its local cache. If no compatible cached version exists, it selects and downloads the newest compatible non-yanked registry version. The editor then rewrites the import with the exact selected version:

import Postgres version 1.2.3

The interval before that rewrite is intentionally nondeterministic. Canonical persisted code normally contains the resolved version, and editor-managed lock state records the complete transitive dependency graph.

Different spaces may explicitly import different versions of the same package. Each version produces a distinct module value and distinct Rust types. Those types are not interchangeable merely because their names match. A single space cannot import two versions of the same package.

Cargo Features

Wrapper metadata maps its Typical feature names to Cargo features. Wrapper-facing feature names are ordinary Typical identifiers; for example, a Cargo feature named native-tls can be exposed as nativeTls. Features required unambiguously by API use are inferred. The editor writes inferred features into the import or applicable declare import automatically.

Some features select behavior that API use cannot reveal, such as a TLS implementation, runtime integration, database backend, compression format, macro support, or bundled native library. Those features must be selected explicitly or through an editor decision.

Cargo unions all requested features for one package version across the final dependency graph. Typical follows that rule even when the feature requests originate in different lexical spaces. Features for different package versions remain separate. Mutually incompatible features produce a notice and dependency-resolution recovery.

Canonical Package Names

Ordinary wrapper names derive from Cargo package names. Hyphens and underscores divide words, and wrapper metadata applies Typical's .NET-style casing:

tokio-postgres -> TokioPostgres
serde_json     -> SerdeJson
html-emitter   -> HtmlEmitter
sql-service    -> SqlService
ip-location    -> IPLocation
sqlx           -> Sqlx

Two-letter initialisms remain uppercase. Longer initialisms use ordinary word casing. Wrapper metadata preserves recognized initialisms when the Cargo spelling alone is insufficient to recover them.

Built-in module names are reserved. In the rare case where a Cargo package collides with a built-in or otherwise cannot be named unambiguously, its exact Cargo name may be quoted and must be aliased:

import Path
import "path" as CratePath version 1.2.3

Quoted package names are an escape hatch, not the ordinary package syntax. They undergo the same cache-first version resolution and editor pinning as canonical package imports.

File Imports

A quoted path ending in .ty, or a glob that selects .ty files, imports Typical source code rather than a wrapper package:

import "CodeFile.ty"
import "path/to/CodeFile.ty"
import "**.ty"

Paths resolve relative to the file containing the import. File imports cannot specify package versions or Cargo features.

Without an alias, the imported file's exported top-level items enter the current space and are inherited by child spaces:

import "CodeFile.ty"

With an alias, an implicit space contains those exports:

import "CodeFile.ty" as CodeFile

CodeFile.run()

Glob expansion has no guaranteed order. Code must not depend on the order in which a glob discovers files. When an explicit file must precede a glob, it may be written first:

import "File.ty"
import "**.ty"

Depending on this ordering is discouraged. If the glob also selects File.ty, the repeated selection is silently deduplicated and the explicit import retains precedence. Other repeated file inclusions and file-import cycles produce notices. As recovery, the first inclusion supplies the file's exports and later repeated or cycle-closing inclusions are ignored.

Placement And Scope

Imports and declare import forms must precede every other semantic member of their containing space. Comments and intents may precede or appear between them.

The implicit project-root space and every explicit space follow the same rule. Classes, functions, starters, control-flow blocks, and other constructs are not spaces and cannot contain imports.

An import in the implicit root space is visible project-wide. An import in an explicit space is visible in that space and its child spaces. Source files do not create lexical scopes. Aliases and inherited imports follow ordinary lexical-scoping rules.

Within one space, the first import of a package creates its module binding. A later import of that package produces a notice and creates neither another alias nor another startup entry, even when it requests a different version.

Module Values

A module import creates a runtime module value:

import Postgres version 1.2.3

entries = Object.entries(Postgres)

Module values may be assigned, passed, returned, stored, and inspected. Each resolved wrapper package and version has one shared module value per program, so imports and aliases of that version refer to the same value.

Module values are frozen. Code cannot add, replace, or remove their members. Wrapped functions and mutable Rust statics may still modify their own underlying state through their defined APIs.

Generated wrappers expose Rust APIs implicitly rather than through Typical visibility groups. Exposure is best-effort, and the exact static and runtime surface may evolve as wrapper generation improves. The generated wrapper and its documentation define what a particular package version exposes, including what appears through runtime inspection.

An imported module binding cannot be exported or re-exported. Only items explicitly defined within a Typical space may be exported from that space.

Startup

Wrapper metadata may define a generated startup function. Every valid wrapper imported anywhere in the compiled project participates in eager startup, even when its binding is unused or appears inside an otherwise unused space. Lexical scope controls visibility, not initialization. The same behavior applies to test entry points.

Startup functions are synchronous and cannot use await. A module that requires asynchronous or fallible initialization must expose an ordinary function for project starter code to call explicitly.

Startup functions should be independent. When static analysis finds that one startup function accesses another imported module, the compiler records a dependency and topologically orders the generated calls. Independent startup functions have no semantic order, and programs must not rely on incidental emitted order.

Each wrapper package version starts at most once. A startup dependency cycle produces notices; every startup function in the cyclic component is short-circuited through unknowable recovery.

Startup functions cannot return bombs. Attempting to do so produces a notice. If a bomb occurs at runtime, execution of that startup function stops.

The compiler injects wrapper startup calls into the final generated Rust main before running project starters. Rust library crates do not provide the executed program entry point; the generated Typical wrapper supplies the startup hook and metadata.

Invalid Imports And Recovery

Typical reports invalid imports with notices rather than compilation errors. A malformed, misplaced, unavailable, or unresolvable module import creates an internal unknowable module placeholder. Values derived from it are also unknowable, and affected statements are short-circuited so the rest of the program remains executable.

An invalid wrapper schedules no startup. unknowable is compiler recovery state, not a user-expressible type or a value intended for shipped code. Its propagation rules are defined in Unknowable Recovery.

Unsupported TypeScript Forms

Typical does not support TypeScript-style import forms or dynamic imports:

import { readFile } from "fs"
import Fs from "fs"
import * as Fs from "fs"
import type { Thing } from "thing"
const Fs = await import("fs")
import("fs")

Package Management And Rust Lowering

An import is both a lexical binding and a direct wrapper dependency declaration. The compiler and package manager resolve it, consult their caches and registry, generate the required Cargo project information in the build-output directory, and lower calls through the generated wrapper.

Package discovery, downloading, locking, feature resolution, wrapper generation, platform selection, and Rust project generation are package-manager responsibilities. They never require a second hand-authored dependency manifest.

The software bill of materials is an editor concern. The editor derives BOM, dependency, version, feature, license, and supply-chain views from imports, wrapper metadata, and resolved lock state rather than representing the BOM as ordinary source code.