Assertion Functions
Assertion functions — специальные функции TypeScript, которые при выполнении сужают тип аргумента в вызывающем коде: если функция не бросила исключение, компилятор считает условие доказанным.
Зачем нужно
Позволяют инкапсулировать проверку инварианта в одной функции и при этом получить narrowing в вызывающем коде — без повторных type guard-условий. Это особенно удобно в коде с assert-паттерном, когда программа должна упасть при нарушении предусловия.
Где используется
- Валидация входных параметров в контроллерах и сервисах
- Assertion-хелперы в тестах (
assertDefined,assertType) - Работа с
unknownданными после парсинга JSON/конфига - Замена повторяющихся
if (!x) throwв бизнес-логике
Основной контент
Assertion function — функция с сигнатурой asserts value или asserts value is Type. Если она возвращает нормально (не бросает), компилятор обновляет тип переменной в вызывающем scope.
asserts condition
function assert(condition: unknown, message: string): asserts condition {
if (!condition) {
throw new Error(message);
}
}
function processUser(user: User | null) {
assert(user !== null, "user must not be null");
// здесь user: User — narrowing произошёл
console.log(user.name);
}
asserts value is Type
function assertIsString(val: unknown): asserts val is string {
if (typeof val !== "string") {
throw new TypeError(`Expected string, got ${typeof val}`);
}
}
function greet(input: unknown) {
assertIsString(input);
// здесь input: string
console.log(input.toUpperCase());
}
Assertion functions vs type guards
// Type guard — возвращает boolean, narrowing только внутри if
function isString(val: unknown): val is string {
return typeof val === "string";
}
// Assertion function — бросает, narrowing после вызова
function assertString(val: unknown): asserts val is string {
if (typeof val !== "string") throw new TypeError("Not a string");
}
// Использование type guard
if (isString(x)) {
// x: string здесь
}
// Использование assertion function
assertString(x);
// x: string отсюда и до конца блока
Реальный пример: парсинг конфига
interface Config {
port: number;
host: string;
}
function assertIsConfig(val: unknown): asserts val is Config {
if (
typeof val !== "object" ||
val === null ||
typeof (val as Config).port !== "number" ||
typeof (val as Config).host !== "string"
) {
throw new Error("Invalid config shape");
}
}
const raw: unknown = JSON.parse(process.env.APP_CONFIG ?? "{}");
assertIsConfig(raw);
// raw: Config — типобезопасно дальше
console.log(`Starting on ${raw.host}:${raw.port}`);
Частые ошибки
- Не указать
assertsв сигнатуре — функция будет обычной, narrowing не произойдёт. - Вернуть значение вместо броска — assertion function должна возвращать
void; любойreturn valueвызовет ошибку компилятора. - Путать с type guard — type guard возвращает
booleanи сужает тип внутриif, assertion function сужает после вызова. - Использовать в async функциях — narrowing после
await assertSomething(x)пока не поддерживается стабильно; лучше проверять синхронно.
Связанные темы
- Пользовательские Type Guards
- Type Guards -- typeof и instanceof
- any, unknown -- различия
- _MOC TypeScript