typescript-expert

Глубокая экспертиза в системе типов TypeScript, продвинутых паттернах и best practices.

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "typescript-expert" with this command: npx skills add dengineproblem/agents-monorepo/dengineproblem-agents-monorepo-typescript-expert

TypeScript Expert

Глубокая экспертиза в системе типов TypeScript, продвинутых паттернах и best practices.

Core Principles

Foundational Guidelines

typescript_principles:

  • name: "Type Safety Priority" guideline: "Always prefer strict type checking and avoid any type" reason: "Catch errors at compile time, not runtime"

  • name: "Smart Inference" guideline: "Let TypeScript infer types when they're obvious" reason: "Reduce noise while maintaining safety"

  • name: "Immutability Preference" guideline: "Use readonly and as const for unchangeable data" reason: "Prevent accidental mutations"

  • name: "Discriminated Unions" guideline: "Apply tagged unions for managing complex state" reason: "Exhaustive type checking and better DX"

  • name: "Explicit Public APIs" guideline: "Always type public function signatures explicitly" reason: "Documentation and contract enforcement"

Type System Fundamentals

Basic Types

// Primitive types const name: string = "John"; const age: number = 30; const isActive: boolean = true; const nothing: null = null; const notDefined: undefined = undefined; const unique: symbol = Symbol("id"); const bigNumber: bigint = 9007199254740991n;

// Arrays const numbers: number[] = [1, 2, 3]; const strings: Array<string> = ["a", "b", "c"]; const readonly: readonly number[] = [1, 2, 3]; // immutable

// Tuples const tuple: [string, number] = ["age", 30]; const namedTuple: [name: string, age: number] = ["John", 30]; const optionalTuple: [string, number?] = ["John"]; const restTuple: [string, ...number[]] = ["sum", 1, 2, 3];

// Objects const user: { name: string; age: number } = { name: "John", age: 30 }; const optionalProps: { name: string; age?: number } = { name: "John" }; const readonlyObj: Readonly<{ name: string }> = { name: "John" };

Union and Intersection Types

// Union types (OR) type Status = "pending" | "active" | "completed"; type StringOrNumber = string | number;

// Intersection types (AND) type Named = { name: string }; type Aged = { age: number }; type Person = Named & Aged; // { name: string; age: number }

// Discriminated unions (tagged unions) type Result<T, E = Error> = | { success: true; data: T } | { success: false; error: E };

function handleResult<T>(result: Result<T>): T | null { if (result.success) { return result.data; // TypeScript knows data exists } console.error(result.error); // TypeScript knows error exists return null; }

// Exhaustive checking type Shape = | { kind: "circle"; radius: number } | { kind: "square"; side: number } | { kind: "rectangle"; width: number; height: number };

function getArea(shape: Shape): number { switch (shape.kind) { case "circle": return Math.PI * shape.radius ** 2; case "square": return shape.side ** 2; case "rectangle": return shape.width * shape.height; default: // Exhaustive check - compiler error if case is missing const _exhaustive: never = shape; return _exhaustive; } }

Advanced Type System

Generics

// Basic generics function identity<T>(value: T): T { return value; }

// Multiple type parameters function pair<T, U>(first: T, second: U): [T, U] { return [first, second]; }

// Generic constraints interface Lengthwise { length: number; }

function logLength<T extends Lengthwise>(item: T): T { console.log(item.length); return item; }

// Generic with default type interface Container<T = string> { value: T; }

// Constrained generics function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; }

// Generic classes class Stack<T> { private items: T[] = [];

push(item: T): void { this.items.push(item); }

pop(): T | undefined { return this.items.pop(); }

peek(): T | undefined { return this.items[this.items.length - 1]; } }

// Generic with multiple constraints function merge<T extends object, U extends object>(a: T, b: U): T & U { return { ...a, ...b }; }

Utility Types

// Built-in utility types interface User { id: number; name: string; email: string; password: string; createdAt: Date; }

// Partial - all properties optional type PartialUser = Partial<User>;

// Required - all properties required type RequiredUser = Required<PartialUser>;

// Readonly - all properties readonly type ReadonlyUser = Readonly<User>;

// Pick - select specific properties type UserCredentials = Pick<User, "email" | "password">;

// Omit - exclude specific properties type PublicUser = Omit<User, "password">;

// Record - create object type with specific keys type UserRoles = Record<string, "admin" | "user" | "guest">;

// Exclude - exclude types from union type NonNullableString = Exclude<string | null | undefined, null | undefined>;

// Extract - extract types from union type StringsOnly = Extract<string | number | boolean, string>;

// NonNullable - remove null and undefined type NonNullUser = NonNullable<User | null | undefined>;

// ReturnType - get function return type function createUser() { return { id: 1, name: "John" }; } type UserReturn = ReturnType<typeof createUser>;

// Parameters - get function parameters as tuple type CreateUserParams = Parameters<typeof createUser>;

// ConstructorParameters - get constructor parameters class UserClass { constructor(public name: string, public age: number) {} } type UserConstructorParams = ConstructorParameters<typeof UserClass>;

// InstanceType - get instance type from constructor type UserInstance = InstanceType<typeof UserClass>;

// Awaited - unwrap Promise type type ResolvedUser = Awaited<Promise<User>>;

Conditional Types

// Basic conditional type type IsString<T> = T extends string ? true : false;

type A = IsString<string>; // true type B = IsString<number>; // false

// Conditional type with infer type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

type Resolved = UnwrapPromise<Promise<string>>; // string type NotPromise = UnwrapPromise<number>; // number

// Extract return type from function type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

// Extract array element type type ArrayElement<T> = T extends (infer E)[] ? E : never; type Element = ArrayElement<string[]>; // string

// Distributive conditional types type ToArray<T> = T extends any ? T[] : never; type Distributed = ToArray<string | number>; // string[] | number[]

// Non-distributive (wrap in tuple) type ToArrayNonDistributive<T> = [T] extends [any] ? T[] : never; type NonDistributed = ToArrayNonDistributive<string | number>; // (string | number)[]

// Practical example: Deep readonly type DeepReadonly<T> = T extends (infer U)[] ? DeepReadonlyArray<U> : T extends object ? DeepReadonlyObject<T> : T;

interface DeepReadonlyArray<T> extends ReadonlyArray<DeepReadonly<T>> {}

type DeepReadonlyObject<T> = { readonly [P in keyof T]: DeepReadonly<T[P]>; };

Template Literal Types

// Basic template literals type Greeting = Hello, ${string}!; type ValidGreeting = "Hello, World!"; // valid // type InvalidGreeting: "Hi, World!" // error

// Event names type EventName<T extends string> = on${Capitalize&#x3C;T>}; type ClickEvent = EventName<"click">; // "onClick"

// CSS units type CSSUnit = "px" | "em" | "rem" | "%"; type CSSValue = ${number}${CSSUnit};

const width: CSSValue = "100px"; // valid const height: CSSValue = "50%"; // valid

// Getter/Setter patterns type Getters<T> = { [K in keyof T as get${Capitalize&#x3C;string &#x26; K>}]: () => T[K]; };

type Setters<T> = { [K in keyof T as set${Capitalize&#x3C;string &#x26; K>}]: (value: T[K]) => void; };

interface Person { name: string; age: number; }

type PersonGetters = Getters<Person>; // { getName: () => string; getAge: () => number }

// Route patterns type HTTPMethod = "GET" | "POST" | "PUT" | "DELETE"; type Route = /${string}; type Endpoint = ${HTTPMethod} ${Route};

const endpoint: Endpoint = "GET /users"; // valid

Mapped Types

// Basic mapped type type Optional<T> = { [K in keyof T]?: T[K]; };

// Mapped type with modifier removal type Concrete<T> = { [K in keyof T]-?: T[K]; // remove optional };

type Mutable<T> = { -readonly [K in keyof T]: T[K]; // remove readonly };

// Key remapping type PrefixedKeys<T, P extends string> = { [K in keyof T as ${P}${Capitalize&#x3C;string &#x26; K>}]: T[K]; };

interface Config { host: string; port: number; }

type PrefixedConfig = PrefixedKeys<Config, "server">; // { serverHost: string; serverPort: number }

// Filter keys by value type type FilterByType<T, U> = { [K in keyof T as T[K] extends U ? K : never]: T[K]; };

interface Mixed { name: string; age: number; active: boolean; email: string; }

type StringProps = FilterByType<Mixed, string>; // { name: string; email: string }

// Nullable all properties type Nullable<T> = { [K in keyof T]: T[K] | null; };

// Create event handlers type EventHandlers<T> = { [K in keyof T as on${Capitalize&#x3C;string &#x26; K>}Change]: (newValue: T[K]) => void; };

type PersonHandlers = EventHandlers<Person>; // { onNameChange: (newValue: string) => void; onAgeChange: (newValue: number) => void }

Type Guards

Built-in Type Guards

// typeof guard function processValue(value: string | number) { if (typeof value === "string") { return value.toUpperCase(); // TypeScript knows it's string } return value.toFixed(2); // TypeScript knows it's number }

// instanceof guard class Dog { bark() { console.log("Woof!"); } }

class Cat { meow() { console.log("Meow!"); } }

function speak(animal: Dog | Cat) { if (animal instanceof Dog) { animal.bark(); } else { animal.meow(); } }

// in operator guard interface Fish { swim: () => void; }

interface Bird { fly: () => void; }

function move(animal: Fish | Bird) { if ("swim" in animal) { animal.swim(); } else { animal.fly(); } }

Custom Type Guards

// Type predicate function interface Admin { role: "admin"; permissions: string[]; }

interface User { role: "user"; email: string; }

type Person = Admin | User;

function isAdmin(person: Person): person is Admin { return person.role === "admin"; }

function handlePerson(person: Person) { if (isAdmin(person)) { console.log(person.permissions); // Admin type } else { console.log(person.email); // User type } }

// Assertion function function assertIsString(value: unknown): asserts value is string { if (typeof value !== "string") { throw new Error("Value must be a string"); } }

function processInput(input: unknown) { assertIsString(input); console.log(input.toUpperCase()); // TypeScript knows it's string }

// Non-null assertion function assertDefined<T>(value: T | null | undefined): asserts value is T { if (value === null || value === undefined) { throw new Error("Value must be defined"); } }

Function Types

Function Signatures

// Function type annotation type MathOperation = (a: number, b: number) => number;

const add: MathOperation = (a, b) => a + b; const subtract: MathOperation = (a, b) => a - b;

// Call signatures in interfaces interface Calculator { (a: number, b: number): number; description: string; }

const multiply: Calculator = Object.assign( (a: number, b: number) => a * b, { description: "Multiplies two numbers" } );

// Overloads function createElement(tag: "a"): HTMLAnchorElement; function createElement(tag: "canvas"): HTMLCanvasElement; function createElement(tag: "table"): HTMLTableElement; function createElement(tag: string): HTMLElement; function createElement(tag: string): HTMLElement { return document.createElement(tag); }

const anchor = createElement("a"); // HTMLAnchorElement const canvas = createElement("canvas"); // HTMLCanvasElement

// Generic function with constraints function firstElement<T extends { length: number }>(arr: T): T[0] | undefined { return arr[0]; }

// Function with this parameter interface Button { label: string; click(this: Button): void; }

const button: Button = { label: "Submit", click() { console.log(this.label); }, };

Rest Parameters and Spread

// Rest parameters function sum(...numbers: number[]): number { return numbers.reduce((acc, n) => acc + n, 0); }

// Typed rest parameters function formatMessage(template: string, ...values: (string | number)[]): string { return values.reduce<string>( (msg, val, i) => msg.replace({${i}}, String(val)), template ); }

// Variadic tuple types type Concat<T extends unknown[], U extends unknown[]> = [...T, ...U];

type Result = Concat<[1, 2], [3, 4]>; // [1, 2, 3, 4]

// Labeled tuple elements function readButtonInput(...args: [name: string, version: number, ...input: boolean[]]) { const [name, version, ...input] = args; }

Classes and OOP

Class Fundamentals

class Animal { // Property declarations public name: string; protected species: string; private _age: number; readonly id: string;

// Static members static kingdom = "Animalia";

// Constructor constructor(name: string, species: string, age: number) { this.name = name; this.species = species; this._age = age; this.id = crypto.randomUUID(); }

// Getters and setters get age(): number { return this._age; }

set age(value: number) { if (value < 0) throw new Error("Age cannot be negative"); this._age = value; }

// Methods speak(): void { console.log(${this.name} makes a sound); }

// Static methods static isAnimal(obj: unknown): obj is Animal { return obj instanceof Animal; } }

// Inheritance class Dog extends Animal { breed: string;

constructor(name: string, age: number, breed: string) { super(name, "Canis familiaris", age); this.breed = breed; }

// Override method override speak(): void { console.log(${this.name} barks!); } }

Abstract Classes

abstract class Shape { abstract readonly name: string; abstract getArea(): number; abstract getPerimeter(): number;

// Concrete method describe(): string { return This is a ${this.name} with area ${this.getArea()}; } }

class Circle extends Shape { readonly name = "circle";

constructor(public radius: number) { super(); }

getArea(): number { return Math.PI * this.radius ** 2; }

getPerimeter(): number { return 2 * Math.PI * this.radius; } }

class Rectangle extends Shape { readonly name = "rectangle";

constructor(public width: number, public height: number) { super(); }

getArea(): number { return this.width * this.height; }

getPerimeter(): number { return 2 * (this.width + this.height); } }

Interfaces vs Types for Classes

// Interface for class implementation interface Serializable { serialize(): string; deserialize(data: string): void; }

interface Comparable<T> { compareTo(other: T): number; }

class User implements Serializable, Comparable<User> { constructor(public id: number, public name: string) {}

serialize(): string { return JSON.stringify({ id: this.id, name: this.name }); }

deserialize(data: string): void { const parsed = JSON.parse(data); this.id = parsed.id; this.name = parsed.name; }

compareTo(other: User): number { return this.id - other.id; } }

// Mixins pattern type Constructor<T = {}> = new (...args: any[]) => T;

function Timestamped<TBase extends Constructor>(Base: TBase) { return class extends Base { createdAt = new Date(); updatedAt = new Date();

touch() {
  this.updatedAt = new Date();
}

}; }

function Activatable<TBase extends Constructor>(Base: TBase) { return class extends Base { isActive = false;

activate() {
  this.isActive = true;
}

deactivate() {
  this.isActive = false;
}

}; }

class BaseEntity { id = crypto.randomUUID(); }

const TimestampedActivatableEntity = Timestamped(Activatable(BaseEntity)); const entity = new TimestampedActivatableEntity(); entity.activate(); entity.touch();

Module System

Export Patterns

// Named exports export const API_URL = "https://api.example.com"; export type UserID = string; export interface User { id: UserID; name: string; }

export function fetchUser(id: UserID): Promise<User> { return fetch(${API_URL}/users/${id}).then((r) => r.json()); }

// Default export export default class UserService { async getUser(id: string): Promise<User> { return fetchUser(id); } }

// Re-exports export { User as UserModel } from "./user"; export * from "./types"; export * as utils from "./utils";

// Type-only exports export type { User, UserID };

Declaration Files

// types.d.ts - Ambient declarations

// Declare module for untyped package declare module "untyped-library" { export function doSomething(value: string): number; export const version: string; }

// Extend existing module declare module "express" { interface Request { user?: { id: string; role: string; }; } }

// Global declarations declare global { interface Window { myApp: { version: string; config: Record<string, unknown>; }; }

namespace NodeJS { interface ProcessEnv { NODE_ENV: "development" | "production" | "test"; API_URL: string; DATABASE_URL: string; } } }

// Ambient namespace declare namespace MyNamespace { interface Config { apiKey: string; baseUrl: string; }

function initialize(config: Config): void; }

Best Practices

Error Handling

// Result type pattern type Result<T, E = Error> = | { ok: true; value: T } | { ok: false; error: E };

function ok<T>(value: T): Result<T, never> { return { ok: true, value }; }

function err<E>(error: E): Result<never, E> { return { ok: false, error }; }

async function fetchUser(id: string): Promise<Result<User, string>> { try { const response = await fetch(/api/users/${id}); if (!response.ok) { return err(HTTP ${response.status}: ${response.statusText}); } const user = await response.json(); return ok(user); } catch (e) { return err(e instanceof Error ? e.message : "Unknown error"); } }

// Usage async function handleUser(id: string) { const result = await fetchUser(id);

if (result.ok) { console.log(result.value.name); } else { console.error(result.error); } }

Immutability Patterns

// as const for literal types const config = { api: { url: "https://api.example.com", timeout: 5000, }, features: ["auth", "analytics"], } as const;

type Config = typeof config; type Feature = (typeof config.features)[number]; // "auth" | "analytics"

// Readonly utilities interface State { user: User | null; items: Item[]; settings: Settings; }

type ImmutableState = Readonly<State>; type DeepImmutableState = { readonly [K in keyof State]: Readonly<State[K]>; };

// Immutable update pattern function updateState<T extends object, K extends keyof T>( state: T, key: K, value: T[K] ): T { return { ...state, [key]: value }; }

Strict Null Checks

// Handling nullable values function processUser(user: User | null | undefined): string { // Optional chaining const name = user?.name ?? "Anonymous";

// Nullish coalescing const email = user?.email ?? "no-email@example.com";

// Non-null assertion (use sparingly!) // const id = user!.id;

return ${name} (${email}); }

// Type narrowing function getLength(value: string | null): number { if (value === null) { return 0; } return value.length; // TypeScript knows it's string }

// Assert non-null with custom message function assertNonNull<T>( value: T | null | undefined, message: string ): asserts value is T { if (value === null || value === undefined) { throw new Error(message); } }

tsconfig.json Best Practices

{ "compilerOptions": { // Strict type checking "strict": true, "noImplicitAny": true, "strictNullChecks": true, "strictFunctionTypes": true, "strictBindCallApply": true, "strictPropertyInitialization": true, "noImplicitThis": true, "useUnknownInCatchVariables": true, "alwaysStrict": true,

// Additional checks
"noUnusedLocals": true,
"noUnusedParameters": true,
"exactOptionalPropertyTypes": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,

// Module resolution
"moduleResolution": "bundler",
"module": "ESNext",
"target": "ES2022",
"lib": ["ES2022", "DOM", "DOM.Iterable"],

// Output
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "./dist",
"rootDir": "./src",

// Interop
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"forceConsistentCasingInFileNames": true,
"isolatedModules": true,
"resolveJsonModule": true,

// Path mapping
"baseUrl": ".",
"paths": {
  "@/*": ["./src/*"],
  "@/components/*": ["./src/components/*"],
  "@/utils/*": ["./src/utils/*"]
}

}, "include": ["src//*"], "exclude": ["node_modules", "dist", "/*.test.ts"] }

Лучшие практики

  • Включай strict mode — это основа type safety

  • Избегай any — используй unknown для неизвестных типов

  • Явно типизируй публичные API — функции, классы, интерфейсы

  • Используй discriminated unions для сложных состояний

  • Применяй readonly для иммутабельных данных

  • Создавай type guards вместо type assertions

  • Используй template literal types для строковых паттернов

  • Организуй типы в отдельные файлы — .types.ts, index.d.ts

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

bug-bounty-program

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

sales-development-rep

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

learning-development-plan

No summary provided by upstream source.

Repository SourceNeeds Review