5 ms·
hmm... I've been using JS/TS for almost as long as they've existed. A lot of these are nice. Some less so. Some quick thoughts: - Tagged template strings. This
by itslennysfault 4y ago
hmm... I've been using JS/TS for almost as long as they've existed. A lot of these are nice. Some less so. Some quick thoughts:
- Tagged template strings. This just feels dirty to me. Probably won't use, but when I see it in a code base I won't be so confused at least
- matchAll. I've never needed this. I've used match with g a bunch, but I never need the capture group.
- Promise.allSettled. THIS is useful. I've implemented this (under a different name) in almost every code base I've worked on, and got bit HARD by not understanding this behavior long ago (huge production outage that took hours and many engineers to discover)
- globalThis. EW. Don't think I have to elaborate
- replaceAll. It's always annoyed me needing to use RegEx for simple replace all. so Yay!
- ??=, &&=, ||= These seem really useful, but also potentially hard to read, but I think if I get used to their existence they'd become second nature
- # private... not sure why they didn't just use the "private" keyword, but I don't care. I almost always use TypeScript anyways
- static ... YAY! finally. Again, if they could do this i don't see why not "private"
For the TypeScript stuff I'll just say the type system has kinda jumped the shark, but I don't hate it. It's SO robust and of all the new stuff being added I'll maybe use 1/10 of it, but it's good to know I can describe literally anything* with it if needed.
* EXCEPT IF I WANT TO USE AN ENUM/TYPE AS A KEY IN AN DICT WHICH I REALLY WANT TO DO!!
- maxpowa 4y agoWRT Enum as key in object: enum TestEnum { Fizz = 0, Buzz, Bar, Baz } type EnumKeyedObject = Record<TestEnum, string>; type EnumKeyedObjectAlt = { [P in TestEnum]: string };
- mjhay 4y agoThat's still awkward and confusing for what would be one of the most common use-cases, if it were less awkward.
- throwanem 4y agoHow would you simplify the syntax here?
- itslennysfault 4y agoI'd like to be able to just do... type UserType = 'default' | 'admin' | 'manager'; interface UserTypeCounts { [key: UserType]: number } Not the best example, but it gets the point across. When you do this it says the key must be a string type which it actually is. It's just a string limited to specific values. Yes, I could do an interface with explicitly named keys instead, but if that type (or enum) could have dozens of possible values it's annoying to duplicate it.
- manv1 4y ago"when I see it in a code base I won't be so confused at least" When I first started using node way back when I discovered all kinds of idioms in use that I had never seen. It was a confusing few weeks for sure.
- treis 4y ago>* EXCEPT IF I WANT TO USE AN ENUM/TYPE AS A KEY IN AN DICT WHICH I REALLY WANT TO DO!! It's better just to use an actual array for enums: myEnum = ["E1", "E2"...] as const type myEnum = typeof myEnum[number] That gets you both an enum type and an enum array you can use at runtime
- anamexis 4y agoI've used globalThis for polyfills in code that needs to run both in the browser and on node.
- progx 4y ago"??=, &&=, ||=" Yes they are, i stuck always when i see them. I think we just need more practice and need to see them more often until it becomes normal. Currently i avoid to use them.
- tengbretson 4y agoRegarding your last point, I think the general consensus in TypeScript is to avoid using enums entirely.
- itslennysfault 4y agoWhat about union string types? I'd really like to be able to just do... type UserType = 'default' | 'admin' | 'manager'; interface UserTypeCounts { [key: UserType]: number }
- Izkata 4y ago> - Tagged template strings. This just feels dirty to me. Probably won't use, but when I see it in a code base I won't be so confused at least What's funny is the go-to example of using it for translations is just wrong: It only does numeric indexing, so can't be reliably used with languages where words would be in a different order. You still need a library or something that builds on top of it to handle that.
- edflsafoiewq 4y agoRealistically changing word order isn't enough for translation either, you need a special language like ICU message syntax so you can handle grammatical number, gender, etc.
- hn_throwaway_99 4y ago> - Tagged template strings. This just feels dirty to me. Probably won't use, but when I see it in a code base I won't be so confused at least Tagged template strings are an absolutely brilliant feature and have tons of valuable uses. In particular, many sql libraries in node let you do this: const query = sql`select foo from bar where zed = ${param}`; From a developer standpoint it "feels" just like you're doing string concatenation, but in reality the query variable will contain a prepared statement so that it safely prevents any kind of SQL injection, e.g. it gets parsed to { sql: "select foo from bar where zed = ?", parameters: [param] } There are lots of use cases where things are easily expressed as an interpolated string, but the thing you want back is NOT just a plain string, and tagged template literals are great for that. It's also a nice way to call a parser, e.g. many GraphQL libraries let you do: const parsedGraphQLSchema = gql`type Query { foo: Int }`;
- fulafel 4y agoIs it really an good thing that a vulnerable sql string interpolation code pattern and this sql tagged string look and feel the same?
- sanitycheck 4y agoI was thinking the same thing... If I do that I'm going to forget the "sql" part at least once and nothing's going to alert me about it.
- hn_throwaway_99 4y agoThe way libraries work it's impossible to forget the "sql" part and still have that query be executed - see my sibling comment.
- hn_throwaway_99 4y agoActually, yes, it is. The way these libraries work, since the thing that is parsed is NOT just a plain string, in most cases it's impossible to have sql injection without doing some deliberately nasty stuff. That is, you can't just do this: const query = `select foo from bar where zed = ${param}`; // forgot the sql tag await runQuery(query); In that case, the type of query is just string, but the `runQuery` method doesn't take strings, it takes a parsed query, so that wouldn't work. After using the tagged template literal pattern for SQL queries exclusively for the past couple years, I can't say enough how awesome it is to use in practice. Libraries even let you do strong typing with TypeScript to define the expected structure of the result, e.g. sql<MyExpectedReturnType>`select foo from bar where zed = ${param}`
- stasm 4y ago> - # private... not sure why they didn't just use the "private" keyword, but I don't care. I almost always use TypeScript anyways One of the reasons was to allow private and public fields of the same name, so that subclasses are free to add own public fields without accidentally discovering private fields of superclasses. There were many more considerations that went into the design: https://github.com/tc39/proposal-class-fields/blob/main/PRIVATE_SYNTAX_FAQ.md https://github.com/tc39/proposal-class-fields/blob/main/PRIV.... There was a heated debate about this and the choice of the # sigil back in 2015 at the time private fields were being designed: https://github.com/tc39/proposal-private-fields/issues/14 https://github.com/tc39/proposal-private-fields/issues/14.
- 5Qn8mNbc2FNCiVV 4y agoIsn't Record<EnumType, whatever> working? Then instantiate like: ``` { [EnumType.First]: ..., [EnumType.Second]: ... } ```
- hn_throwaway_99 4y agoYep, do this all the time. It's also really nice because when you define a type as Record<EnumType, any> then when you are creating an instance of that Record object, it requires a key for all the EnumType values, and will fail if you forgot one. Still possible to do Partial<Record<EnumType, any>> if you want the keys to be only of the EnumType, but don't require all the EnumType values to be used as a key in the Record.
- illiarian 4y ago> Tagged template strings. This just feels dirty to me. Probably won't use, but when I see it in a code base I won't be so confused at least A while back I wrote that "Tagged Template Literals Are the Worst Addition to Javascript" https://dmitriid.com/blog/2019/03/tagged-template-literals/ https://dmitriid.com/blog/2019/03/tagged-template-literals/ and I still stand by it. The fact that someone uses them in a somewhat nice fashion in an sql library doesn't change the fact
- cagmz 4y ago> - Promise.allSettled. THIS is useful. I've implemented this (under a different name) in almost every code base I've worked on, and got bit HARD by not understanding this behavior long ago (huge production outage that took hours and many engineers to discover) You and your team didn't understand how Promise.allSettled behaved?
- hn_throwaway_99 4y agoI'm guessing the more common error, which I've definitely hit years ago, is not knowing that Promise.all returns immediately if any of the included promises reject.
- cagmz 4y agoThat's what I'm guessing, too.
- itslennysfault 4y agoCorrect. It was millions of lines of code in a huge code base at a giant enterprise company with 100s of engineers. Somewhere buried in there there was a Promise.all that someone assumed would finish ALL of the promises, and didn't account for the fact that it bails on the first error.
- itslennysfault 4y agoNo, Promise.allSettled didn't exist yet. It was Promise.all. Someone basically made the assumption that await Promise.all would wait until ALL promises finished. Which is true..... unless one of them throws. In which case it continues. This caused a race condition. It was an extremely complex code base with 100s of engineers and the error very rarely happened, and when it did happen the app would get stuck on a loading screen forever. Also, it turns out "rarely" happens to a lot of people when you have millions of users.