Проверка того, что неверный вызов отвергается при компиляции — и остаётся отвергнутым
createUser не должен принимать пользователя без email. Тест в рантайме этого не покажет: неверный вызов не компилируется, поэтому раннеру нечего выполнять.
Требования: утвердить в тестовом файле, что вызов ниже — ошибка компиляции, и сделать так, чтобы само утверждение валило сборку в тот день, когда вызов станет законным, и гарантия не сгнила незаметно. Считайте, что файл входит в tsconfig, который проверяет CI.
interface NewUser { name: string; email: string }
declare function createUser(u: NewUser): void;
// your code here
createUser({ name: 'Ada' });
Допишите реализацию.
Поставьте @ts-expect-error над неверным вызовом. Он компилируется, только пока эта строка всё ещё падает, и сам становится ошибкой в день, когда вызов примут — поэтому утверждение не сгниёт, в отличие от вечно молчащего @ts-ignore. Файл обязан попадать в проверку типов.
- ✗Пытаться проверить ошибку компиляции рантайм-проверкой
toThrow— код не компилируется, выполнять нечего - ✗Брать
@ts-ignore, который продолжает проходить после исчезновения ошибки и гниёт в мёртвую директиву - ✗Оставлять файл с тестами типов вне
tsconfig, который проверяет CI, из-за чего утверждение не вычисляется
- →Почему
@ts-expect-errorвалит сборку, когда ошибка под ним исправлена? - →Как утверждать точный выведенный тип возврата, а не отвергнутый вызов?
Решение
import { expectTypeOf } from 'expect-type';
interface NewUser { name: string; email: string }
declare function createUser(u: NewUser): void;
// @ts-expect-error — email обязателен; если вызов станет законным, упадёт эта строка
createUser({ name: 'Ada' });
// и точная проверка формы параметра
expectTypeOf(createUser).parameter(0).toEqualTypeOf<NewUser>();
Почему это работает
@ts-expect-error — не просто глушилка. Он требует, чтобы ошибка на следующей строке была. Пока createUser({ name: 'Ada' }) не проходит проверку типов, файл собирается. В тот день, когда кто-то сделает email необязательным, ошибка исчезнет — и компилятор сообщит Unused '@ts-expect-error' directive. Сборка упадёт, и о потерянной гарантии узнают сразу.
@ts-ignore так не умеет: он молчит и когда ошибка есть, и когда её нет, поэтому проверка тихо превращается в мёртвый комментарий.
⚠️ Всё это работает только если файл попадает в tsc --noEmit в CI. Раннер, который просто транспилирует тесты (esbuild, SWC), типы не проверяет — и утверждение не сработает никогда.