Эта статья — вольный перевод свежего стрима ThePrimeTimeagen (бывший разработчик Netflix, харизматичный чувак с усами)
"I'm just the normy, but everyone is so bad at their crap, so I just rise to the top"
Цитата:
«Я просто обычный разработчик, но все вокруг настолько плохо справляются со своей работой, что на их фоне я кажусь топовым»
Я понимаю что вся эта информация в текстовом виде не очень то похожа на стендап, поэтому очень советую посмотреть оригинальный стрим. Многие примеры я видела впервые и мне было реально смешно :)
WTF №1 — Проверка лишних свойств
У нас есть тип, который имеет свойства firstName
и age
, а также функция, которая принимает объект типа Person
и просто печатает его:
type Person = { firstName: string, age: number };
function logPerson(person: Person) {
console.log(person);
}
Чтобы вызвать функцию, мы просто сделаем вот так:
logPerson({ firstName: 'trash', age: 21 });
И это — совершенно ожидаемое поведение. Если мы передадим лишнее поле, то получим ошибку:
logPerson({ firstName: 'trash', age: 21, extraProp: 21 }); // ошибка
Object literal may only specify known properties, and 'extraProp' does not exist in type 'Person'.ts(2353)

Но есть одна интересная деталь: если мы вынесем объект в переменную и передадим её в функцию, ошибки не возникает:
const person = {
firstName: 'trash',
age: 21,
extraProp: 21
};
logPerson(person); // нет ошибки
Мы можем отловить ошибку, если укажем тип объекта явно:
const person: Person = {
firstName: 'trash',
age: 21,
extraProp: 21 // Object literal may only specify known properties, and 'extraProp' does not exist in type 'Person'.ts(2353)
};
Но даже в этом случае, если мы очень захотим добавить дополнительное свойство, то можно обойти ограничение:
const person: Person = {
firstName: 'trash',
age: 21,
...{ extraProp: 21 } // ошибки нет
};
WTF №2 — Enums
У нас есть Enum с цветами и функция, которая принимает конкретное значение из этого Enum. Если мы передадим Colors.Green
, всё работает. Но если передать строку, совпадающую по значению, то ничего не получится
enum Colors {
Green = 'green',
Orange = 'orange',
Red = 'red',
}
function isTheBestColor(color: Colors) {
// some code
}
isTheBestColor(Colors.Green); // работает ❤️
isTheBestColor('green'); // не работает ?
Но...
Если использовать Enum с числовыми значениями:
enum Status {
Pending = 0,
Declined = 1,
Approved = 2,
}
function validateStatus(status: Status) {
// some code
}
// работает ❤️
validateStatus(Status.Declined);
// работает!? ??
validateStatus(1);
Некоторые разработчики предпочитают такую запись, чтобы обойти Enum:
const Foo = {
FAILED: 'failed',
SUCCESS: 'success',
} as const;
type Bar = typeof Foo;
type BarKeys = keyof Bar;
type BarValues = Bar[BarKeys];
const x = (f: BarKeys) => f;
x('FAILED');
const y = (f: BarValues) => f;
y('failed');
Такая запись позволяет при вызове соответствующей функции сразу выбирать из предзаполненных вариантов и видеть конкретное значение в реальном времени при логировании
WTF №3 — .filter()
Существует популярный способ отфильтровать все falsy-значения с помощью filter
const arr = [1, undefined, 3].filter(Boolean);
console.log(arr); // [1, 3]
В этом примере мы ожидаем, что тип arr будет number[], но на самом деле получаем (number | undefined)[], потому что TypeScript не может точнее определить тип возвращаемого значения. Это неудобно: ты точно знаешь, что в рантайме undefined не будет, но в типах обязан это учитывать
WTF №4 — {} vs object vs Object

Существует несколько способов описать объект.
Когда мы хотим создать пустой объект, обычно пишем let foo = {}
, но забавно, что мы можем присвоить этой переменной любое примитивное значение — кроме null
и undefined
. Если же задать тип явно как object
, он работает ожидаемо — не принимает примитивы
let foo: object;
foo = { hello: 0 }; ✅
foo = []; ✅
foo = false; ❌
foo = null; ❌
foo = undefined; ❌
foo = 42; ❌
foo = 'bar'; ❌
А теперь let foo: {}
foo = { hello: 0 }; ✅
foo = []; ✅
foo = false; ✅
foo = null; ❌
foo = undefined; ❌
foo = 42; ✅
foo = 'bar'; ✅
WTF №5 — Бонусный
У нас есть массив строк и функция, которая принимает массив строк или чисел. Ошибки нет, хотя после изменения тип массива остаётся прежним. WTF?
const foo: string[] = ["1", "2"];
function bar(thing: (number | string)[]) {
thing.push(3);
}
bar(foo);
console.log(foo); // ['1', '2', 3]
Комментарии (10)
jeny_tat
14.07.2025 05:21Мне про TypeScript понравился коммент одного разраба: "TypeScript делает простые вещи сложными, а сложные вещи простыми". И в итоге Макрософт пытались с помощью TypeScript обуздать "чудовище" (JS), которое они однажды создали, но как-то не туда все пошло опять
Alexandroppolus
14.07.2025 05:21С пунктом 4 есть ещё более интересный прикол. Да, в object напрямую не засунуть примитив, но можно так:
const nn: {} = 1; const obj: object = nn;
То есть
object
оказался надтипом для более широкого{}
, что идет вразрез с иерархией типов.П.3 широко известен, во многих библиотеках есть его исправление, добавляющее перегрузку для метода filter.
П.5 - следствие ковариантности изменяемого массива, добавленной для удобства и в своё время вызвавшей много споров.
Vitaly_js
14.07.2025 05:21По моему, стоит задиприкейтить {} потому что сейчас это пережиток прошлого.
Vitaly_js
14.07.2025 05:21В этом примере мы ожидаем, что тип arr будет number[], но на самом деле получаем (number | undefined)[], потому что TypeScript не может точнее определить тип возвращаемого значения. Это неудобно: ты точно знаешь, что в рантайме undefined не будет, но в типах обязан это учитывать
Нет. Все обстоит не так. Начиная с версии 5.5 Typescript корректно выводит предикаты типов. Но должны соблюдаться два условия:
1. Типа предикат должен возвращать тип Т, когда функция возвращает true
2. Когда функция возвращает false входной параметр точно не Т
А что мы видимо в предложенном примере? Когда у нас значение 0, то Boolean возвращает false. Поэтому Boolean не является корректным предикатом типов для number. Поэтому typescript и не меняет number | undefined на number. И правильно делает, потому что тогда бы разработчики допускали ошибку в коде.
alpo-tech
Переходим на rust