Pureline TypeScript
Strong boundaries, inferred internals.
Pureline TypeScript inherits all Pureline JavaScript rules. On top comes a clear type model: explicit types at API boundaries, inference inside, unknown instead of any, and unions instead of ceremony.
Inherits all rules from Pureline JavaScript.
Profile rules
Avoid `any`
any turns the type system off. Truly unknown data is unknown and is validated at the boundary.
function load(value: any): any {
} const config = JSON.parse(raw) as unknown as Config; function load(value: UserId): User {
} function parse(input: unknown): Config {
} Use Types Deliberately
Both inferred and explicit local types are Pureline-compliant. An annotation is not redundant merely because TypeScript could infer the type.
Explicit types are especially useful when they make intent clearer, document an expected contract, protect a boundary from unintended widening, improve refactoring confidence, or make code easier to understand without following an entire call chain.
Type inference should remove noise. It should never become a rule against using TypeScript's type system.
let state = "idle";
// state ist jetzt string, jeder Tippfehler kompiliert
state = "redy"; const user = loadUser(); const user: User = loadUser(); let state: SessionState = "idle"; No Java-Style `I` Prefix
TypeScript interfaces are named after their role. The I prefix is profile-specific: Java and Emvus use it; TypeScript does not.
interface ISession {
} interface Session {
} `interface` vs `type`
Use interface for object-like contracts. Use type for unions, aliases, and callable shapes.
type User = {
id: string;
name: string;
}; interface Handler {
(packet: Packet): Promise<void>;
} interface User {
id: string;
name: string;
} type SessionState = "ready" | "closed" | "failed"; type Handler = (packet: Packet) => Promise<void>; Prefer Union Types Over Enums When Appropriate
Use an enum only when an actual enum construct provides value. For simple states, a union type is clearer.
enum State {
Idle = "idle",
Ready = "ready",
Closed = "closed"
} type State = "idle" | "ready" | "closed"; Strong Types at Boundaries, Freedom Internally
Public APIs, external data, persistence boundaries, and network payloads should normally expose clear types.
Inside an implementation, both explicit annotations and inference are valid. Choose the form that makes the code easiest to understand. Pureline does not score one as cleaner purely because it contains fewer type annotations.
export async function openSession(request, options) {
const payload = await fetch(url).then(response => response.json());
return sessions.create(payload);
} export async function openSession(request: Request): Promise<Session | null> {
const user = await users.load(request.userId);
if (!user) {
return null;
}
return sessions.create(user);
} Folder Density
A TypeScript feature/source folder may contain at most **10 production source modules** by default.
Barrel files such as index.ts, declaration-only support files, tests, generated files, and metadata-only files do not count.
A split should represent a real responsibility boundary.
session/
session/state/
session/transport/
session/storage/ Core rules in the TypeScript checker
In addition to the profile rules, the TypeScript checker verifies these rules from the Pureline Core: