1 В избранное 0 Ответвления 0

OSCHINA-MIRROR/openharmony-arkcompiler_ets_frontend

 / Детали:

[Новое требование]: Устранить простые типы данных

Предстоит сделать
Владелец
Создано  
05.03.2025

Новые требования предоставляют какие возможности?

Устранение примитивных типов

Мотивация

Примитивные типы являются странным отдельным компонентом системы типов ArkTS: они
не находятся в субтипировании с другими типами (включая Object), они не могут быть частью объединённых типов или использоваться как аргументы типа для шаблонов.

Чтобы имитировать эти возможности, ArkTS предоставляет "большие буквы" эквиваленты всем этим типам (Number для number и т.д.). Когда это необходимо, происходит неявное преобразование (оба направления, "упаковка" и "распаковка"), что усложняет семантический анализ программ на ArkTS. Кроме того, синтаксически можно записывать конструкции, такие как double | undefined или Set<number>, которые на самом деле интерпретируются как Double | undefined и Set<Number> соответственно; это может ввести в заблуждение программиста и приводить к непредвиденному поведению в некоторых случаях.

С точки зрения инструментов, обработка примитивных типов и вставка операций "упаковки"/"распаковки" усложняет компилятор es2panda и приводит к постоянному потоку ошибок.

Предлагаемое решение(Использую number/Number как пример примитивного значения в остальной части текста. Я буду называть эти типы примитивно-подобными. Я использую словоформу "текущий язык", чтобы указать текущее состояние спецификации — с примитивными типами как отдельной сущностью, и "предложенный язык" для языка без примитивных типов.)Удалите понятие примитивного типа из определения языка ArkTS; приблизительно сделайте number синонимом текущего Number, подклассом Object.

Можно также сохранить синонимы void/Void, undefined/Undefined, null/Null, never/Never, string/String. Для каждой пары этих типов большие и маленькие буквы будут относиться к одному и тому же типу.

Арифметические и другие операции применяются напрямую к Number и т. д., без промежуточной распаковки. Эти типы могут участвовать в объединении типов или использоваться как аргументы типа для шаблонов.

Операторы сравнения == и === должны сравнивать эти объекты по значению, как они делают для string.

Поскольку все эквиваленты текущих примитивных типов являются неизменяемыми, такие операции, как ++ и --, создают новые объекты. (Точно так же, в настоящее время код, такой как

let x: Number;
x++;

эквивалентен следующему:

let x: Number;
x = new Number(x.value + 1);

) Это тот же стратегический подход, который используется в Kotlin: примитивные типы данных являются просто особым случаем объектов; факт того, что в байткоде они представлены как примитивы, является лишь оптимизацией компилятора.Некоторые фрагменты кода начинают означать одно и то же:

Применимо к примитивам Применимо к упакованным примитивам
let x = 1.0 let xx = new Number(1.0)
x++ xx++ в текущем языке, упакованная версия означает xx = new Number(xx.value + 1)
x == 0 xx == 0 версия с Number в текущем языке означает xx.unboxed() == 0
x === 0 xx === 0 одинаково
function f(xx: Number){}; f(x) function f(x: number){}; f(xx) текущий язык указывает на неявное упаковывание и распаковывание
function g<T>(t: T){}; g(x) function g<T>(t: T){}; g(xx) в текущем языке, версия с number включает неявное упаковывание
`let u: number null = 0` `let uu: Number
g<number>(0) g<Number>(0) снова, number в текущем языке неявно заменяется на Number (и аргумент упаковывается)
typeof(x) == "number" typeof(xx) == "number" Оба конструкта в настоящее время возвращают одинаковое значение, поэтому ничего не меняется
В настоящий момент применимо к примитивам Применимо к упакованным примитивам
--- --- ---
x.toString() xx.toString() в текущем языке методы не могут применяться к примитивам
x instanceof Object xx instanceof Object CTE в настоящее время слева. После изменения обе версии возвращают true

Конструкция xx.unboxed() становится устаревшей (возвращает просто xx), но может быть сохранена для целей совместимости.

Некоторые конструкции становятся нелегальными (см. раздел Сложности):

let x: int = 0
let y = x as long  // смысл ключевого слова `as` должен быть ограничен

Реализация На стороне реализации в большинстве случаев объекты простых типов будут храниться как примитивные значения в регистрах или полях, а упаковка будет выполняться только тогда, когда потребуется рассматривать эти значения как экземпляры класса Object.- использование их в позиции получателя вызова метода обычно требует упаковки (но см. ниже относительно внутренних операций);

  • вызов метода, который принимает параметр общего типа, специализированного для Number, требует упаковки;
  • возврат значения из метода, который возвращает параметризованный тип, специализированный для Number, требует распаковки;
  • переопределение метода общего класса версией, специализированной для простого типа, потребует передачи/возврата аргументов в упакованной форме, см. panda#20984;
  • хранение значения в поле с типом параметра, специализированного для Number, требует упаковки; чтение из такого поля требует распаковки;
  • хранение значения простого типа в переменной объединённого типа требует упаковки;
  • получение значения из такой переменной требует распаковки, включая случаи, где истинный тип значения выводится из умной проверки. Внутри компилятора es2panda все такие манипуляции могут либо
  • ограничиваться генератором кода, либо
  • выполняться как отдельная стадия понижения сразу перед генератором кода.Все ранние стадии могут игнорировать понятие типа, похожего на примитивный тип (за исключением, возможно, элементов, перечисленных в следующем разделе).

Рассмотрим пример (я использую модифицированный и оптимизированный вручную выход текущего es2panda):

функция id<T>(x: T) { return x }

структура C<T> {
  f1: число
  f2: T
}

функция основная() {
	предварительная переменная x: число = 1
        fldai.64 0x3ff0000000000000  // 1.0
  		sta.64 v0                    // нет коробок

нет коробок -> нет коробок (необходимо проверить контекст)

Финальный текст:

функция id<T>(x: T) { return x }

структура C<T> {
  f1: число
  f2: T
}

функция основная() {
	предварительная переменная x: число = 1
        fldai.64 0x3ff0000000000000  // 1.0
  		sta.64 v0                    // нет коробок
```	предварительная переменная y = x + 2.0
	    fldai.64 0x4000000000000000  // 2.0
		fadd2.64 v0                  // всё ещё нет коробок
		sta.64 v1
	
	предварительная переменная z: число = id<число>(x)
	    lda.64 v0
		вызов.acc.короткий std.core.Double.valueOf:(f64), v0, 0x0  // коробка перед передачей как аргумент с параметром
		sta.obj v6
		вызов.короткий ETSGLOBAL.id:(std.core.Object), v6
		checkcast std.core.Double
	   	вызов.acc.короткий std.core.Double.unboxed:(std.core.Double), v0, 0x0 // распаковка после получения из параметра
		sta.64 v2                    // хранение распакованного значения
	
    // Если бы мы сохранили всё в коробках, то x и z содержали бы одинаковый объект; теперь даже если мы заключим их в коробки, они будут представлять собой различные экземпляры.
    // Но нет способа проверить равенство ссылок в нашем языке, так что всё в порядке
	
	x++
		ldai 0x1            
		i32tof64            // нет коробок; с точки зрения определения языка,
		fadd2.64 v0         // x теперь содержит другой объект
		sta v0
		
	предварительная переменная u: число | null = x
		lda.64 v0
		вызов.acc.короткий std.core.Double.valueOf:(f64), v0, 0x0 // коробка перед сохранением в союзе
		sta.obj v3	предварительная переменная m = u!
		lda.obj v3
		вызов.acc.короткий std.core.Double.unboxed:(std.core.Double), v0, 0x0 // распаковка при извлечении из союза
		sta.64 v4
		
	предварительная переменная c = новый C<число>
		initobj.короткий C._ctor_:(C)
		sta.obj v5

	c.f1 = x
		lda.64 v0               // поле примитивного типа, нет коробок
		stobj.64 v5, C.f1

	c.f2 = x
		lda.64 v0
		вызов.acc.короткий std.core.Double.valueOf:(f64), v0, 0x0
		stobj.obj v5, C.f2   // поле типа параметра, коробка
		
	x.toString()
	    lda.64 v0
		вызов.acc.короткий std.core.Double.valueOf:(f64), v0, 0x0  // требуется коробка перед вызовом метода
		вызов.acc.короткий std.core.Double.toString:(std.core.Double), v0, 0x0

Как мы видим, сгенерированная в текущей версии языка основная часть кода остается такой же.

В некоторых случаях простые правила, описанные выше, приведут к немедленному распаковыванию, за которым последует упаковывание:
id(x).toString()
    lda.64 v0
	call.acc.short std.core.Double.valueOf:(f64), v0, 0x0  // упаковка перед передачей как аргумента общего типа
	sta.obj v6
	call.short ETSGLOBAL.id:(std.core.Object), v6
	checkcast std.core.Double
    call.acc.short std.core.Double.unboxed:(std.core.Double), v0, 0x0 // распаковка после получения из общего типа
	call.acc.short std.core.Double.valueOf:(f64), v0, 0x0  // упаковка перед вызовом метода
	call.acc.short std.core.Double.toString:(std.core.Double), v0, 0x0
Однако простое оконное оптимизирование сможет удалить такие избыточные операции.В других случаях значение, хранящееся как примитив, будет упаковываться несколько раз вместо одного раза. Например, следующий код
```ts
let x: number = 0;
id(x);
id(x);

будет (если оптимизатор недостаточно умён) выполнять упаковку перед каждым вызовом общего типа, создавая два упакованных объекта. В текущей версии языка можно было бы сохранить упакованный тип в переменной и использовать его повторно:

let x: number = 0;
let xx: Number = x;
id(xx);
id(xx);
```Но случаи, когда генерируемый код является некачественным, должны встречаться достаточно редко и маловероятно приводить к значительному снижению производительности. С другой стороны, в текущей версии язык позволяет программисту выбрать некачественное представление для чисел, что также приводит к избыточному упаковыванию-распаковыванию:

```ts
let xx: Number = 0;
xx++;
xx++;

Для каждого оператора увеличения текущий компилятор генерирует код для распаковывания и упаковывания. По этому предложению будет выбран оптимальный примитивный тип.

Проблемы

Операторы сравнения == и ===

Эти операторы проверяют равенство ссылок для большинства типов, но проверяют равенство значений для

  • примитивных типов,
  • строк и больших целых чисел.

Они также имеют специальное значение для void и undefined (и это единственная ситуация, где == и === различаются).

Поскольку поведение уже имеет специальный случай для строк и больших целых чисел, новая версия не становится более сложной и странной.

typeof


Теперь текст полностью переведён и оформлен согласно правилам перевода.Оператор typeof должен вернуть специальные значения для упакованных примитивных типов. (Также как он делает сейчас.)

|---|---|
| `typeof(33 as byte) // "number"` | `typeof(33.byteValue()) // "number"` |
| `typeof(33 as char) // "number"` | `typeof(33.charValue()) // "number"` |
| `typeof(33 as short) // "number"` | `typeof(33.shortValue()) // "number"` |
| `typeof(33) // "number"` | `typeof(33) // "number"` |
| `typeof(33 as long) // "number"` | `typeof(33.longValue()) // "number"` |
| `typeof(33.0 as float) // "number"` | `typeof(33.0.floatValue()) // "number"` |
| `typeof(33.0) // "number"` | `typeof(33.0) // "number"` |
| `typeof("33") // "string"` | `typeof("33") // "string"` |
```### Массивы

Хранение упакованных примитивов в массивах является неэффективным.

- Вариант 1. (Похожий на Kotlin) Использование упакованных значений в массивах, таких как `number[]`.

Для этого нам потребуется набор специальных библиотечных классов, соответствующих незапакованным массивам примитивных значений, таких как `Int8Array`, ... `Float64Array`. Эти классы будут служить более эффективной альтернативой массивам, встроенным в язык. Однако эти классы гораздо менее удобны для использования, поэтому большинство кода, написанного или наследованного от TypeScript, скорее всего, будет недопустимым.

- Вариант 2. (Изменение времени выполнения) Изменение поведения `starr.obj` и `ldarr.obj`.  1. Всегда создавайте массивы типа `number[]` как массивы примитивных значений.
  2. Когда компилятор знает, что он работает с массивом примитивного типа, он может использовать обычные команды байткода, такие как `starr` и `ldarr`.
  3. Когда компилятор не уверен, какой тип массива используется (массив примитивных значений или объектов), он использует `starr.obj` и `ldarr.obj`.
  4. `starr.obj`, когда массив состоит из примитивных значений, распаковывает элемент, который требуется сохранить, и сохраняет это распакованное значение. В противном случае он действует так же, как текущий `starr.obj` (то есть работает с типами ссылочных данных).
  5. Аналогично, `ldarr.obj`, когда массив состоит из примитивных значений, предоставляет доступ к элементу массива и его запаковывает, после чего запакованное значение сохраняется в аккумулятор.С этим предложением следующий код может быть сгенерирован:

```ts
function f(nn: number[], v: number): number {
  let ov = nn[0];
  nn[0] = v;
  return ov;
}
        movi v0, 0;
        lda v0;
        fldarr.64 a0;  // специализированные версии команд байткода массивов
        sta.64 v1;
        lda.64 a1;
        starr.64 a0, v0;
        lda.64 v1;
        return.64;
```функция g<T>(nn: T[], v: T): T {
  let ov = nn[0];
  nn[0] = v;
  return ov;
}
        movi v0, 0;
        lda v0;
        ldarr.obj a0;   // объектные версии массивных байткодов
        sta.obj v1;     // должна работать с примитивными массивами также
        lda.obj a1;
        starr.obj a0, v0;
        lda.obj v1;
        return.obj;

пусть a0: number[] = [1, 2, 3];

f(a0, 11);
        call.short ETSGLOBAL.f(f64[], f64), v0, v1;  // прямой вызов, ничего не упаковывается
g(a0, 11);
        lda.64 v1;
        call.acc.short std.core.Double.valueOf:(f64), v0, 0x0;  // скалярный аргумент упаковывается
        // массивный аргумент передается как есть, модифицированные байткодные инструкции позаботятся об этом
        call.short ETSGLOBAL.g(std.core.Object[], std.core.Object);

### Преобразования `as`

В текущем языке оператор `as` выполняет две различных функции в зависимости от типов, задействованных:

```typescript
let v: S;
v as T;
  • когда оба типа S и T являются ссылочными типами, v as T просто проверяет и переинтерпретирует значение v; новый объект не создается

  • когда либо S, либо T являются примитивными типами, v as T выполняет преобразование, создавая новое значение (это никогда не происходит в TypeScript).Существуют два варианта решения этой проблемы:

  • указать специальное поведение для оператора as для примитивных типов, чтобы сохранить совместимость с существующими кодами на ArkTS;или

  • [моё предпочитаемое решение, которое может быть реализовано независимо от остальной части предложения]
    Как в TypeScript (или Kotlin), as всегда выполняет проверку типа и переинтерпретацию значения. Примитивным типам потребуются специальные методы преобразования, так что, например, текущий код

3 as long

будет приводить к ошибке компиляции. Он будет выглядеть следующим образом:

3.longValue()

Кроме того, все числовые типы будут продолжать реализовывать все методы класса Numeric:

экспортировать абстрактный класс Numeric расширяющий Объект {
    публичный абстрактный байтValue(): байт;
    публичный абстрактный интValue(): int;
    публичный абстрактный шортValue(): short;
    публичный абстрактный лонгValue(): long;
    публичный абстрактный флоатValue(): float;
    публичный абстрактный даблValue(): double;
}

Компилятору потребуются внутренние инструменты компиляции (panda#15204) для создания эффективного кода при вызовах этих методов. Было бы крайне приятно избавиться от всех неявных преобразований,
включая явные преобразования с помощью ключевого слова as. На данный момент мы имеем следующие типы преобразований помимо boxing и unboxing:

  • enum в целое число
  • string enum в строку
  • односимвольная строковая константа в char
  • неявные числовые преобразования между различными примитивными типами данных.## Другие соображения### Перечисления (Enums)

Необходимо отметить, что в текущем языке перечисления обрабатываются
(почти так же, как предлагается здесь) для всех неприсваиваемых типов.

Целочисленные перечисления хранятся в регистре как базовые целочисленные значения.
Однако когда они используются как аргумент типа шаблона или компонент объединения,
каждое значение автоматически преобразуется в обертывающий класс. Например, в следующем фрагменте кода

enum E { ONE, TWO, THREE }

let e: E = E.ONE;
let ee: E | null = e;

значение e записывается в байт-коде как целое число, но ee содержит ссылку на синтетический класс #E; преобразование происходит прозрачно при присвоении. ee instanceof Object вернёт true.

Нет способа создать объект класса #E напрямую; этот класс невидим программисту. Спецификация говорит, что перечисления считаются либо значением, либо ссылочным типом, но она не указывает, что происходит, когда они участвуют в объединениях или служат аргументами типа. (Могу я пропустить это?)

С этим предложением перечисления должны стать подтипами Object; их представление как примитивов будет оптимизацией, аналогичной примитивным типам.

Кортежи, литеральные типыЯ не вижу влияния этого предложения на другие типы данных:

  • кортежи остаются видом ссылочного типа. Если они реализованы поверх массивов (как в текущем es2panda), члены кортежей примитивных типов могут требовать упаковки (как сейчас). Если мы перейдём к реализации кортежей через классы, то эти члены можно будет хранить в неупакованной форме.
  • у нас есть только строковые литеральные типы. Строки не затронуты данным предложением.### Остаточные параметры (Rest Parameters)

Остаточные параметры, объявленные как массивы примитивных типов, могут представляться как массивы примитивов:

function f(x: number, ...xs: number[]): number

может быть представлен как

.function f64 f(f64 a0, f64[] a1)

то же самое, как сейчас.

Корутины (Coroutines)

Это предложение не оказывает влияния на реализацию корутин и асинхронных вычислений.

Совместимость JS/TS (JS/TS interoperability)

  • вызов ArkTS из JS

Это обязательно потребует некоторых преобразований на границе языков, поэтому мы можем преобразовать предоставленный JS объект (number или Number) в тот формат, который ожидает байт-код ArkTS.

  • Вызов JS с помощью ArkTS

опять же, у нас есть возможность преобразовать каждый аргумент, похожий на примитив, в объект-"прокси" JS. Поскольку значение является неизменяемым, мы можем выбрать настоящий объект JS без какого-либо поведения прокси. На мой взгляд, лучшим вариантом будет число.

Нативная совместимость

  • Вариант 1.
    Нативная совместимость действительно потребует различия между примитивными типами и их упакованными версиями, работая с сигнатурами байткода функций и временными представлениями данных. Однако, учитывая что примитивные типы хранятся и передаются в незапакованной форме по возможности, я ожидаю, что влияние текущего предложения на нативную совместимость будет минимальным.- Вариант 2.
    Можно всегда использовать одну или другую форму (упакованную или незапакованную) при взаимодействии с нативным кодом и выполнять конвертации на границе языка. Это похоже на то, что предлагается для JS, но в данном случае затраты времени выполнения могут оказаться слишком большими.

================================= ЭПИК ==============================================

Подготовка упакованных типов

  • Разрешить неявные преобразования между упакованными примитивными типами (например, от Int до Double) 1 ММ
  • Оптимизация временного представления для "упакованных" примитивов; распаковка их на уровне байткода 1.5 ММ
    • поля класса
    • параметры функции
    • типы возврата функции
  • мосты для переопределений 1 ММ

Переключение представления

  • Сделать int ссылаться на GlobalIntType() вместо GlobalIntegerBuiltinType() и т.д. 0.5 ММ
  • Убрать путаницу 0.5 ММ
  • Исправить стандартную библиотеку 0.5 ММ

Чистка

  • Удалить код, который становится избыточным
    • флаги упаковки и все, что относится к ним (кроме случаев, когда это всё ещё требуется генератором кода) 0.5 ММ
    • специализированный код для примитивов во всех этапах, предшествующих генерации кода 1 ММ### Какова ценность этого требования? Примеры применения?

Ценность

Этот подход позволяет более эффективно использовать примитивные типы данных, обеспечивая оптимальное использование памяти и повышая производительность. Он также улучшает совместимость с другими языками программирования, такими как JavaScript, что позволяет легко преобразовывать данные между ними.### Примеры применения

  1. Разработка мобильных приложений: Приложения, использующие этот метод, могут эффективнее использовать ресурсы устройства, что важно для мобильных устройств с ограниченными ресурсами.
  2. Создание веб-приложений: Благодаря улучшенной совместимости с JavaScript, можно создавать более сложные и эффективные веб-приложения.
  3. Обработка больших объемов данных: Этот подход может значительно повысить скорость обработки данных и эффективность использования памяти, что особенно важно для систем анализа больших данных.

Некоторые связанные заметки из задач:

Алексей Недория:

  • Удаление примитивов + массивы: варианты дизайна и реализации -- @Nedoria Aleksei 
  • Удаление примитивов + NARK: как это должно работать вместе -- @Бронников Георгий 
  • Удаление примитивов: явный список правил, где после компиляции имеются упакованные/неупакованные примитивы -- @Бронников Георгий 
  • Удаление примитивов + затирание типов: Будет ли возможно удалить затирание типов после удаления примитивов? Необходимы ли какие-либо ограничения? -- @Бронников Георгий 
  • Совместимость: Где будет сломана пользовательская кодировка? Необходимо больше примеров -- @Бронников Георгий 
  • Предоставьте больше примеров кода для предложения -- @Бронников ГеоргийЮра Бронников:

Вопросы относительно удаления примитивов из ArkTS

Я сознательно пропускаю все вопросы, связанные с массивами.

Удаление примитивов vs. NARK: как это должно работать вместе

  1. Основной вопрос заключается в том, чтобы иметь предсказуемые правила — когда и как сторона C++ должна предоставлять упакованные или незапакованные версии в качестве аргументов функциям.
    • следует ли ожидать упакованные или незапакованные версии в качестве значений возврата;
    • какую версию использовать в качестве значений полей.

См. следующий пункт для таких правил.

  1. Возможно предоставление интерфейса, где сторона C++ сама решает использовать упакованные или незапакованные значения, но это будет медленнее.

Ясный список правил, где после компиляции имеются упакованные / незапакованные примитивы

В большинстве случаев типы, аналогичные примитивам, используются в своей примитивной форме. Они упакованы:

  • Когда используется как часть типа союза;
  • как аргумент типа шаблона;
  • как получатель вызова метода (если метод не является внутренним).
class C<T> {
  n1: number;            // примитив хранится
  n2: number | undefined;  // упакован
  n3: T;                 // упакованный объект хранится в поле класса C<number>
}

function f<T>(n4: number,            // примитив на уровне байткода
               n5: number | undefined,  // упакован
               n6: T) {               // упакован
    n4.toString();                    // упакован
}
```Сложность: наследование от шаблонного класса
```ts
class B<T> {
  f: T				// Поле останется упакованным после наследования
  f(arg: T): T
}

class D extends B<number> {
  // Чтобы переопределение работало, es2panda потребуется сгенерировать мост
  override f(arg: number): number {}
}

Наследование от нон-шаблонного класса, переопределение метода

class B {
  f(o: number): void {}
  g(): Object { return new Object }
}

class D extends B {
  override f(o: Object): void {}    // Требуется мост
  override g(): number { return 1 } // Также требует мост
}

Будет ли возможно удалить затирание типа, если мы убьём примитивы? Будут ли необходимы какие-либо ограничения?

  • Я основываю свои рассуждения на предложении [https://confluence-msc.rnd.huawei.com/display/Compilers2025/Type+system+for+Panda+runtime+design+notes].
  • При вызове шаблонной функции можно передать как упакованную, так и незапакованную версию примитивного значения с соответствующим объектом типа как аргументом типа.
  • Когда передается примитив (на уровне времени выполнения/байткода), соответствующие шаблонные байткоды должны работать с примитивными значениями. JIT может создать оптимизированную версию кода для примитивных аргументов.
  • Проблема: на уровне языка источника нет способа указать использование примитива или упакованной версии.

Где может сломаться код пользователя? Нужны больше примеров

Перегрузки:

function f(n: Number) {}
function f(n: number) {}
```Найдено в `stdlib`: `BuiltinArray.sts:tpSpliced`:
```ts
export function toSpliced(self: boolean[], start?: number, delete?: number): boolean[] {
    const len = self.length;
    return toSpliced(self, asIntOrDefault(start, len), asIntOrDefault(delete, len));
}
export function toSpliced(self: boolean[], start: number, delete: number, ...items: boolean[]): boolean[] {
    const len = self.length;
    return toSpliced(self, start as number, delete as number, ...items);
}

Оба варианта переопределяют один более эффективный (с int вместо number/Number).
escompat/Global.sts:

export function isFinite(d: number): boolean {
    return Double.isFinite(d);
}
export function isFinite(d: Number): boolean {
    return d.isFinite();
}

Проверка с помощью instanceof

function gf<T>(v: T): boolean {
    return v instanceof Object;
}

let v = 1;
console.log(gf(v));					// false в TS, true согласно этому предложению
let vv: Number = new Number(1);
console.log(gf(vv));					// true как в TS, так и согласно этому предложению

Однако текущее поведение ArkTS совпадает с предложенным.

Больше примеров кода для предложения

Умные приведения типов:

let m: number = 44;
let o: Object = m;	// явное преобразование не требуется
if (o instanceof number) {
    // Здесь, o имеет тип number, но, вероятнее всего, es2panda будет использовать упакованное значение
    let k = o + 1;
}

(Интересный факт для Kotlin на JVM:)

fun f1(o1: Any, o2: Any) {
    println("${o1 === o2} ${o1 as Int === o2 as Int}");
}

fun f2(o1: Any, o2: Any) {
    println("${o1 as Int === o2 as Int} ${o1 === o2}");
}

fun main() {
    f1(333, 333);	// false true
    f2(333, 333);	// true true
    f1(33, 33);		// true true
}

Комментарий (0)

GitLife Service Account Задача создана
GitLife Service Account добавлено
 
enhancement
label.
GitLife Service Account добавлено
 
waiting_for_assign
label.
Развернуть журнал операций

Вход Перед тем как оставить комментарий

Статус
Ответственный
Контрольная точка
Pull Requests
Связанные запросы на слияние могут быть закрыты после их объединения
Ветки
Дата начала   -   Крайний срок
-
Закрепить/Открепить
Приоритет
Участники(1)
1
https://api.gitlife.ru/oschina-mirror/openharmony-arkcompiler_ets_frontend.git
git@api.gitlife.ru:oschina-mirror/openharmony-arkcompiler_ets_frontend.git
oschina-mirror
openharmony-arkcompiler_ets_frontend
openharmony-arkcompiler_ets_frontend