typescript-strict — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited typescript-strict (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Strict TypeScript conventions: strict mode config, no-any rules, utility types, discriminated unions, branded types, and error handling patterns.
Always enable strict mode. Use this tsconfig.json base:
{
"compilerOptions": {
"strict": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noFallthroughCasesInSwitch": true,
"forceConsistentCasingInFileNames": true,
"exactOptionalPropertyTypes": true,
"verbatimModuleSyntax": true
}
}strict: true EnablesstrictNullChecks — null and undefined are their own typesstrictFunctionTypes — function parameter types are checked strictlystrictBindCallApply — bind, call, apply are typed correctlystrictPropertyInitialization — class properties must be initializednoImplicitAny — no implicit any typesnoImplicitThis — this must have an explicit typealwaysStrict — emit "use strict" in every fileany Rulesunknown when the type is truly unknowninstanceof, typeoffunction parse<T>(input: string): T@ts-expect-error fails if the error is fixed// Bad
function parse(data: any): any {
return JSON.parse(data);
}
// Good
function parse(data: string): unknown {
return JSON.parse(data);
}
// Good — with type guard
function isUser(value: unknown): value is User {
return (
typeof value === "object" &&
value !== null &&
"id" in value &&
"email" in value
);
}
const data: unknown = parse(input);
if (isUser(data)) {
console.log(data.email); // typed as User
}Use built-in utility types instead of manual type construction:
| Utility | Use Case | Example | |
|---|---|---|---|
Partial<T> | All properties optional | Update DTOs: Partial<User> | |
Required<T> | All properties required | Override optionals | |
Pick<T, K> | Select specific properties | `Pick<User, "id" \ | "name">` |
Omit<T, K> | Remove specific properties | Omit<User, "password"> | |
Readonly<T> | All properties readonly | Immutable state | |
Record<K, V> | Object with known key/value types | Record<string, number> | |
Extract<T, U> | Extract union members matching U | `Extract<Status, "active" \ | "pending">` |
Exclude<T, U> | Remove union members matching U | Exclude<Status, "deleted"> | |
NonNullable<T> | Remove null/undefined | `NonNullable<string \ | null> -> string` |
ReturnType<T> | Extract return type of function | ReturnType<typeof fetchUser> | |
Awaited<T> | Unwrap Promise type | Awaited<Promise<User>> -> User |
// Compose utility types for DTOs
interface User {
id: string;
email: string;
name: string;
password: string;
role: "admin" | "user";
createdAt: Date;
}
type CreateUserInput = Omit<User, "id" | "createdAt">;
type UpdateUserInput = Partial<Omit<User, "id" | "createdAt">>;
type PublicUser = Omit<User, "password">;Use discriminated unions for type-safe state management and variant types:
// Define a discriminated union with a literal "type" field
type ApiResult<T> =
| { status: "loading" }
| { status: "success"; data: T }
| { status: "error"; error: Error };
function renderResult<T>(result: ApiResult<T>) {
switch (result.status) {
case "loading":
return <Spinner />;
case "success":
return <DataView data={result.data} />; // data is available
case "error":
return <ErrorView error={result.error} />; // error is available
}
}noFallthroughCasesInSwitchnever default case// Exhaustive switch — compile error if a variant is missed
function assertNever(value: never): never {
throw new Error(`Unhandled variant: ${JSON.stringify(value)}`);
}
function handleEvent(event: AppEvent) {
switch (event.type) {
case "click":
return handleClick(event);
case "keypress":
return handleKeypress(event);
default:
return assertNever(event); // Compile error if new variant is added
}
}// Action types for reducers
type Action =
| { type: "SET_USER"; payload: User }
| { type: "SET_ERROR"; payload: string }
| { type: "RESET" };
// API responses
type Response =
| { ok: true; data: User[] }
| { ok: false; error: string };
// Form field types
type FormField =
| { kind: "text"; value: string; maxLength?: number }
| { kind: "number"; value: number; min?: number; max?: number }
| { kind: "select"; value: string; options: string[] }
| { kind: "checkbox"; value: boolean };Use branded types to prevent mixing semantically different values that share a primitive type:
// Define branded types
type UserId = string & { readonly __brand: "UserId" };
type OrderId = string & { readonly __brand: "OrderId" };
type Email = string & { readonly __brand: "Email" };
// Constructor functions with validation
function UserId(id: string): UserId {
if (!id.startsWith("usr_")) throw new Error("Invalid user ID");
return id as UserId;
}
function Email(value: string): Email {
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value)) {
throw new Error("Invalid email");
}
return value as Email;
}
// Type safety — can't mix them up
function getUser(id: UserId): Promise<User> { /* ... */ }
function getOrder(id: OrderId): Promise<Order> { /* ... */ }
const userId = UserId("usr_abc123");
const orderId = OrderId("ord_xyz789");
getUser(userId); // OK
getUser(orderId); // Compile error — OrderId is not UserIdUse a Result type instead of throwing exceptions for expected errors:
type Result<T, E = Error> =
| { ok: true; value: T }
| { ok: false; error: E };
// Helper constructors
function ok<T>(value: T): Result<T, never> {
return { ok: true, value };
}
function err<E>(error: E): Result<never, E> {
return { ok: false, error };
}
// Usage
async function createUser(input: CreateUserInput): Promise<Result<User, CreateUserError>> {
const existing = await db.findByEmail(input.email);
if (existing) {
return err({ code: "EMAIL_EXISTS", message: "Email already registered" });
}
const user = await db.create(input);
return ok(user);
}
// Caller handles both cases
const result = await createUser(input);
if (result.ok) {
console.log("Created:", result.value.id);
} else {
console.error("Failed:", result.error.message);
}class AppError extends Error {
constructor(
message: string,
public readonly code: string,
public readonly statusCode: number = 500,
public readonly cause?: unknown,
) {
super(message);
this.name = this.constructor.name;
}
}
class NotFoundError extends AppError {
constructor(resource: string, id: string) {
super(`${resource} not found: ${id}`, "NOT_FOUND", 404);
}
}
class ValidationError extends AppError {
constructor(
message: string,
public readonly fields: Record<string, string>,
) {
super(message, "VALIDATION_ERROR", 400);
}
}catch (e: any)new Error("Failed to save", { cause: originalError })// typeof
function format(value: string | number): string {
if (typeof value === "string") return value.trim();
return value.toFixed(2);
}
// instanceof
function handleError(error: unknown): string {
if (error instanceof AppError) return error.code;
if (error instanceof Error) return error.message;
return String(error);
}
// in operator
function getArea(shape: Circle | Rectangle): number {
if ("radius" in shape) return Math.PI * shape.radius ** 2;
return shape.width * shape.height;
}
// Custom type guard
function isDefined<T>(value: T | null | undefined): value is T {
return value !== null && value !== undefined;
}
const users = [getUser(1), getUser(2), null].filter(isDefined);
// type: User[]unknown.filter(Boolean) loses type narrowing; use .filter(isDefined)~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.