Преимущества в том, что в этом случае инструмент эффективно решает свою задачу.
Если под эффективность понимать написание меньшего количества символов, то да. Тогда 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())
}
}
С-но, с объектами и массивами тоже нет — то, что там неявные 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?
}
Хорошо, это не совсем честный пример, поскольку это словарь, а не массив. Но даже если забыть про то, что в массивы в JS сильно отличаются от массивов в Kotlin и позволяют творить такие вещи как:
Кстати, класс Map из ES2015 имеет корректные сигнатуры, но им не удобно пользоваться, он не всегда применим и его сложнее сериализовать/десериализовать в/из JSON.
Так копирасты просто пришлют абуз на DigitalOcean, а те в свою очередь с удовольствием перешлют его вам. Так что свой уютный сервер на популярной площадке — это тоже не вариант.
Правильно. Зачем вообще делать статическую типизацию? Она будет только раздражать и провоцировать использование 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.
Но самое печальное, что разработчики специально не хотят исправлять эту проблему, потому что "тогда будет не удобно работать тем, кто привык отсутствию статической типизации".
Примерно также, только вместо разгребания чем пользуюсь, а чем не пользуюсь, просто кликаю на "Close other tabs". Если что-то действительно нужное закрылось, то всегда можно в History найти.
Можете привести пример как это может выглядеть в 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 классы, но вручную допиливать автоматически генерируемые классы мне кажется не самой разумной идеей.
Например, получаю список сгенерированных классов, для дальнейшей регистрации их в диспетчере. Либо ищу определенное поле объекта и его значение, которое может хранить некоторую мета-нинформацию об объекте.
Если под эффективность понимать написание меньшего количества символов, то да. Тогда JS еще эффективнее.
Причем здесь вообще Flow? Он вообще другим путем шел, и как раз таки в Flow всегда значительно слабее была гибкость и безопасность типов. Вполне вероятно что именно по этому он и не набрал популярности.
Зато Kotlin, где null-safety на отличном уровне и работает именно так, как хотели бы видеть разработчики из того тикета, живет и процветает.
Это ваши догадки. Даже статистика голосов на github (231 за и 2 против) говорит об обратном.
Нет, не должны быть. Объясню на примере:
Именно, что в Kotlin этой проблемы нету. Вот пример приведенного кода выше на Kotlin:
https://pl.kotl.in/eUFfJ_g2B
Так какие же в этом преимущества, кроме того что в противном случае это якобы будет всех раздражать?
Тоже весьма спорное утверждение, Мне нужен, всем кто проголосовал и отписался в тикете, тоже нужен. Так что это уже не никому.
Хорошим решением было бы добавить соответствую настройку в tsconfig.json, но это они тоже не хотят делать по идеологическим причинам.
Еще как мешает, об этом тоже уже все расписано в том тикете.
Во-первых, тогда у вас все объекты будут
number | undefined
. Например,Object.values(foo)
— вернетArray<number | undefined>
, хотя мы на 100% уверены что в данном случае должен быть простоArray<number>
.А во-вторых, есть огромное количество встроенных типов и сторонних библиотек, которые не возможно никак поменять.
Ну и как я уже написал использовать везде Map просто невозможно в силу того, что весь остальной JavaScript мир использует для этих целей именно объекты.
https://pl.kotl.in/SETSpP6Gi
Хорошо, это не совсем честный пример, поскольку это словарь, а не массив. Но даже если забыть про то, что в массивы в JS сильно отличаются от массивов в Kotlin и позволяют творить такие вещи как:
То со словарями совершенно та же беда:
Playground Link
Кстати, класс Map из ES2015 имеет корректные сигнатуры, но им не удобно пользоваться, он не всегда применим и его сложнее сериализовать/десериализовать в/из JSON.
Похоже, что всё-таки пора на Firefox переходить. Там же нету такого мракобесия пока что?
Так копирасты просто пришлют абуз на DigitalOcean, а те в свою очередь с удовольствием перешлют его вам. Так что свой уютный сервер на популярной площадке — это тоже не вариант.
Забавно. Т.е. если у тебя монитор подключен, например, по VGA кабелю, то посмотреть фильм уже не выйдет?
Правильно. Зачем вообще делать статическую типизацию? Она будет только раздражать и провоцировать использование
any
.К слову, оператор
!
совсем не обязательно использовать, а в циклахfor; of
и функциональных методах типаmap
,forEach
итак не будетundefined
. О чем тоже вполне справедливо заметили в комментариях.Я бы добавил в копилку проблем TS некорректное определение сигнатуры массивов в lib.d.ts:
И если проблемы описанные в статье еще можно пофиксить линтерами, то тут ничего не придумать, кроме как форкнуть typescript или писать monkey patch для lib.d.ts.
Но самое печальное, что разработчики специально не хотят исправлять эту проблему, потому что "тогда будет не удобно работать тем, кто привык отсутствию статической типизации".
Обсуждение этого бага можно найти тут.
Вы же в курсе что лигатуры отключить можно? Или это дело принципа?
Открыл одну вкладку, быстро запомнил что там было и сразу закрыл.
Надо так указывать :)
Примерно также, только вместо разгребания чем пользуюсь, а чем не пользуюсь, просто кликаю на "Close other tabs". Если что-то действительно нужное закрылось, то всегда можно в History найти.
Как и 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 классы, но вручную допиливать автоматически генерируемые классы мне кажется не самой разумной идеей.
Например, получаю список сгенерированных классов, для дальнейшей регистрации их в диспетчере. Либо ищу определенное поле объекта и его значение, которое может хранить некоторую мета-нинформацию об объекте.