Примитивы в TS vs реальность V8
TypeScript показывает
string,number,booleanкак "примитивные типы". С точки зрения спецификации ECMAScript и реализации V8 — примитивных типов в JS не существует. Естьprimitive value(значение, не тип) и оптимизации рантайма. Это важно для понимания, почему TS-типы не дают рантайм-гарантий.
Что это / Зачем в TS
Несоответствие модели TS и реальности рантайма:
- В TS
number— это монолитный тип. - В V8
numberможет быть SMI (31 бит на стеке), heap number, или объект-обёртка. - TS-проверка
typeof x === 'string'не отражает того, что строки в V8 — обычные объекты в куче с propertylength,Symbol.iteratorи т.д.
Что говорит спецификация ECMAScript
В разделе 6 ECMAScript описано 7 Language Types: undefined, null, boolean, number, bigint, symbol, string, object. Слова "примитив" в описании типов нет. Есть только понятие primitive value — значение, которое тип может вернуть.
"Они называются примитивами не потому что это тип, а потому что ниже них уже ничего нет."
Что происходит в V8
const s = 'abc';
s.length; // как у строки берётся .length?
На самом деле:
- Доступ к property примитива → внутренняя операция ToObject.
- ToObject вызывает
new String(s)под капотом. - На обёртке вызывается
length.
"На уровне V8 строка — обычный объект в куче со своими property."
Числа до 31 бита (SMI) — оптимизация: V8 кладёт их прямо в указатель, объект не создаётся. Это оптимизация, а не "примитивный тип".
Почему это важно для TS
function process(x: string) {
x.length; // TS считает это property типа string
// На самом деле — это property от new String(x), а не у самого x
}
Следствия:
- Можно переопределить
String.prototype[Symbol.iterator]— и[...'abc']вернёт не символы. - Можно сломать
valueOf/toString/Symbol.toPrimitiveу объекта так, что он будет вести себя как примитив. - TS не видит этих манипуляций — никакая статическая типизация их не обнаружит.
Объект может притворяться примитивом
const num = {
[Symbol.toPrimitive](hint: string) {
if (hint === 'number') return 42;
if (hint === 'string') return '42';
return true;
},
};
num + 1; // 43 (number)
`${num}`; // '42' (string)
num == true; // true
TS-тип этого объекта будет странным, но рантайм-поведение полностью идентично "примитиву".
Когда использовать / Когда НЕ
- ✅ когда: понимаешь, почему TS-типы — это только подсказка IDE, а не рантайм-гарантия
- ✅ когда: дебажишь странности
typeof/instanceof - ✅ когда: пишешь библиотеки с
Symbol.toPrimitiveили Proxy - ❌ когда: бизнес-логика — здесь хватает TS-модели
Альтернативы
- branded types — компенсируют отсутствие nominal typing.
- рантайм-валидация (Zod/Joi) — единственная реальная гарантия типа на границе.
- assert-функции — превращают TS-нарушение в throw в рантайме.
🎓 Источники
- 🎓 Как одни фантазируют на тему типов в JavaScript · AsForJS · 2021-10-10 · YouTube
- Тезисы: примитивных типов нет в спецификации; есть
primitive value; все типы в V8 представлены объектами в куче. - Позиция автора: "В JS примитивных типов не существует."
- Тезисы: примитивных типов нет в спецификации; есть
- 🎓 Я тип простой — я говорю стихами · AsForJS · 2023-03-29 · YouTube
- Тезисы: только string и symbol — настоящие примитивы по критерию (формируют ключ); индекс массива всегда строка; SMI как оптимизация V8.
- 🎓 Существует ли приведение типа в JavaScript · AsForJS · 2025-02-16 · YouTube
Противоречие с автором
автор обычно говорит про "формы объектов" в V8, не углубляясь в спор о терминологии. AsForJS жёстко спорит с термином "примитивный тип". На практике обе позиции не противоречат: TS-аннотации — это static check, а V8 видит всё как объекты с разными оптимизациями.