All streams
Search
Write a publication
Pull to refresh
4
0
Ilya Pirogov @ilyapirogov

Developer

Send message
Преимущества в том, что в этом случае инструмент эффективно решает свою задачу.

Если под эффективность понимать написание меньшего количества символов, то да. Тогда JS еще эффективнее.


Утверждение подтверждено практикой. Флоу, который два года назад...

Причем здесь вообще Flow? Он вообще другим путем шел, и как раз таки в Flow всегда значительно слабее была гибкость и безопасность типов. Вполне вероятно что именно по этому он и не набрал популярности.


Зато Kotlin, где null-safety на отличном уровне и работает именно так, как хотели бы видеть разработчики из того тикета, живет и процветает.


"все, за исключением некоторой с практической точки зрения несущественной доли".

Это ваши догадки. Даже статистика голосов на github (231 за и 2 против) говорит об обратном.


Так они и должны быть все number | undefined. Это же именно то, чего требуется от "корректного типа для мапы".

Нет, не должны быть. Объясню на примере:


const arr1: { [key: number]: string } = {};
arr1[42] = undefined; // Ошибка, TS не даст это сделать
arr1.map(x => x.toLowerCase()); // Соответственно тут все хорошо, т.к. x - это string

const arr2: { [key: number]: string | undefined } = {};
arr1[42] = undefined; //  Нет ошибки
arr1.map(x => x.toLowerCase()); // А тут уже придется страдать, потому что x - это string | undefined

а шо, таки в котлине такой проблемы нет? есть же

Именно, что в Kotlin этой проблемы нету. Вот пример приведенного кода выше на Kotlin:


fun main() {
    // Альтернатива на TS: const arr1: { [key: number]: string } = {};
    val arr1 = mutableMapOf<Int, String>() 
    arr1[43] = null; // Ошибка. String не nullable
    arr1.values.forEach {
        // Котлин гарантирует, что it не null. По этому ошибки нету
        println(it.toUpperCase())
    }

    // Альтернатива на TS: const arr1: { [key: number]: string | undefined } = {};
    val arr2 = mutableMapOf<Int, String?>()
    arr2[43] = null; // OK
    arr2.values.forEach {
        // Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Foo?
        println(it.toUpperCase())
    }
}

https://pl.kotl.in/eUFfJ_g2B

С-но, с объектами и массивами тоже нет — то, что там неявные undefined, это как раз преимущество тс.

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


Т.к. без этого он был бы не нужен никому.

Тоже весьма спорное утверждение, Мне нужен, всем кто проголосовал и отписался в тикете, тоже нужен. Так что это уже не никому.


Хорошим решением было бы добавить соответствую настройку в tsconfig.json, но это они тоже не хотят делать по идеологическим причинам.


И, собственно, вам никто не мешает объявлять так: { [index: string]: number | undefined } = {};

Еще как мешает, об этом тоже уже все расписано в том тикете.


Во-первых, тогда у вас все объекты будут number | undefined. Например, Object.values(foo) — вернет Array<number | undefined>, хотя мы на 100% уверены что в данном случае должен быть просто Array<number>.
А во-вторых, есть огромное количество встроенных типов и сторонних библиотек, которые не возможно никак поменять.


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

data class Bar(val bar: String)
data class Foo(val foo: Bar)

fun main() {
    val arr = mapOf<Int, Foo>()
    println(arr[43].foo.bar) // Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Foo?
}

https://pl.kotl.in/SETSpP6Gi


Хорошо, это не совсем честный пример, поскольку это словарь, а не массив. Но даже если забыть про то, что в массивы в JS сильно отличаются от массивов в Kotlin и позволяют творить такие вещи как:


const arr: string[] = [];
arr[42] = "foo";
if (arr.length > 0) {
    console.log(arr[0].toUpperCase()); // Surprise!
}

То со словарями совершенно та же беда:


const foo: { [key: number]: string } = {};
// TypeScript: It's ok!
// Runtime: TypeError: foo[32] is undefined
console.log(foo[32].toUpperCase());

Playground Link


Кстати, класс Map из ES2015 имеет корректные сигнатуры, но им не удобно пользоваться, он не всегда применим и его сложнее сериализовать/десериализовать в/из JSON.

Похоже, что всё-таки пора на Firefox переходить. Там же нету такого мракобесия пока что?

Так копирасты просто пришлют абуз на DigitalOcean, а те в свою очередь с удовольствием перешлют его вам. Так что свой уютный сервер на популярной площадке — это тоже не вариант.

который дальше идёт шифрованным аж до цифрового видеоинтерфейса, поддерживающего шифрование, типа HDMI

Забавно. Т.е. если у тебя монитор подключен, например, по VGA кабелю, то посмотреть фильм уже не выйдет?

Правильно. Зачем вообще делать статическую типизацию? Она будет только раздражать и провоцировать использование any.


К слову, оператор ! совсем не обязательно использовать, а в циклах for; of и функциональных методах типа map, forEach итак не будет undefined. О чем тоже вполне справедливо заметили в комментариях.

Я бы добавил в копилку проблем TS некорректное определение сигнатуры массивов в lib.d.ts:


const arr: Array<{foo: {bar: string}}> = [];
console.log(arr[43].foo.bar); // this is fine!

И если проблемы описанные в статье еще можно пофиксить линтерами, то тут ничего не придумать, кроме как форкнуть typescript или писать monkey patch для lib.d.ts.


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


Обсуждение этого бага можно найти тут.

Вы же в курсе что лигатуры отключить можно? Или это дело принципа?

Открыл одну вкладку, быстро запомнил что там было и сразу закрыл.

Надо так указывать :)


  • [0..10)
  • [10..20)
  • [20..50)
  • [50..100)
  • [100..∞)

Примерно также, только вместо разгребания чем пользуюсь, а чем не пользуюсь, просто кликаю на "Close other tabs". Если что-то действительно нужное закрылось, то всегда можно в History найти.

nullish coalescing — в тайпскрипте уже есть

Как и Optional Chaining. А все остальное можно полифилами добить.

Немного смущает наличие Visual Studio требованиях и возможные проблемы с .NET Framework 4.5.2. Но в любом случае спасибо, посмотрю.

Либо экзистенциальный тип.

Можете привести пример как это может выглядеть в C#? Хотя, видимо, в любом случае это потребует дополнительной генерации кода, что не приемлемо в моем случае.


А, сторонее приложение не даёт эту информацию в типизированном виде?

В частности, стороннее приложение, это Protocol Buffer: C#. Так что да, его встроенные средства не позволяют обобщать типы и не генерирует словарей парсеров.


Хотя он и генерирует мета информацию о сгенерированных типах, под капотом там все равно все тот же самый
System.Reflection.


Тогда да, это проблема (но проблема стороннего приложения, а не типизации).

Я согласен с этим, но в реальном мире мы часто имеем дело со сторонними приложениями и библиотеками, которые не можем контролировать.

Не уверен что значит "генерируется во время сборки". В .NET Framework есть какой-то свой механизм мета-разработки, который на лету позволяет генерировать компилируемый код? Можно подробнее про это?


Если же речь про написание своего плагина к protoc, то, увы, этот вариант не подходит, так как либо потребует распространения бинарника, либо сильно усложнит использование библиотеки сторонними пользователями.

Я бы еще добавил, что помимо полного отказа от any нужно еще и включать режим strict в tsconfig.json, только тогда будет почти полная гарантия отсутствия "undefined is not a function" и аналогичных ошибок в райнтайме.


"Почти", потому что есть одна дыра в TypeScript, которая рушит всю типо-безопасность даже с максимально строгой проверкой и без использования any.

А код который передает и строку и объект в одной переменной, на мой взгляд, плохо пахнет и имеет признаки непродуманного дизайна.

Так строго типизированный TypeScript как-раз позволяет это делать при помощи union types и type guards. И я бы не назвал это непродуманным дизайном. На мой взгляд, как альтернатива перегрузкам функций это работает весьма неплохо и нисколько не мешает статической типизации.

А что диспетчер с ними делает?

Это не важно. Основная необходимость в рефлексии тут только для того, чтобы иметь возможность десериализовать произвольное Protobuf сообщение. Поскольку, в отличии от JSON, данные переданные через Protobuf можно десериализовать только при помощи парсера соответствующего сообщения.


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


Ну вот: вы уже знаете, что в объекте есть некоторое поле. Скорее всего вы знаете структуру метаинформации. Это всё вполне можно типизировать.

Каким образом, если они генерируются сторонним приложением? Они, конечно, создаются как partial классы, но вручную допиливать автоматически генерируемые классы мне кажется не самой разумной идеей.

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

Information

Rating
Does not participate
Location
Austin, Texas, США
Date of birth
Registered
Activity