Pureline

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.

Strong TypingInferenceunknown > anyUnions & Types
7Profile rules
21automated
Check TypeScript code

Profile rules

PL-TS-001 Checker

Avoid `any`

any turns the type system off. Truly unknown data is unknown and is validated at the boundary.

Avoid
function load(value: any): any {
}
any through the back door
const config = JSON.parse(raw) as unknown as Config;
Pureline
function load(value: UserId): User {
}
Unknown data
function parse(input: unknown): Config {
}
PL-TS-002

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.

Avoid
Unintended widening
let state = "idle";
// state ist jetzt string, jeder Tippfehler kompiliert
state = "redy";
Pureline
Inference when obvious
const user = loadUser();
Explicit, equally valid
const user: User = loadUser();
Explicit protects the contract
let state: SessionState = "idle";
PL-TS-003 Checker

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.

Avoid
interface ISession {
}
Pureline
interface Session {
}
PL-TS-004 Checker

`interface` vs `type`

Use interface for object-like contracts. Use type for unions, aliases, and callable shapes.

Avoid
Object contract as type
type User = {
    id: string;
    name: string;
};
Signature as interface
interface Handler {
    (packet: Packet): Promise<void>;
}
Pureline
Object contract
interface User {
    id: string;
    name: string;
}
Union
type SessionState = "ready" | "closed" | "failed";
Signature
type Handler = (packet: Packet) => Promise<void>;
PL-TS-005 Checker

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.

Avoid
enum State {
    Idle = "idle",
    Ready = "ready",
    Closed = "closed"
}
Pureline
type State = "idle" | "ready" | "closed";
PL-TS-006 Checker

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.

Avoid
Untyped API boundary
export async function openSession(request, options) {
    const payload = await fetch(url).then(response => response.json());
    return sessions.create(payload);
}
Pureline
export async function openSession(request: Request): Promise<Session | null> {
    const user = await users.load(request.userId);

    if (!user) {
        return null;
    }

    return sessions.create(user);
}
PL-TS-007

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.

Pureline
Real responsibility boundaries
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: