enum TestEnum {
Fizz = 0,
Buzz,
Bar,
Baz
}
type EnumKeyedObject = Record<TestEnum, string>;
type EnumKeyedObjectAlt = { [P in TestEnum]: string }; 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.
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.
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
I'd really like to be able to just do...
type UserType = 'default' | 'admin' | 'manager';
interface UserTypeCounts {
[key: UserType]: number
}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.
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 }`; 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}`The tagged template does not return a string in this case?
query = sql`select foo from bar where zed = ${p} order by ${col} asc`;
Unless the lib implements a real SQL parser for the right dialect, it will quote each expression in the same way, and will either fail or produce a broken SQL.The example you gave actually isn't valid, because what you're doing is generating SQL dynamically, and that doesn't work the way prepared statements work. That is, you can't have a prepared statement like "select foo from bar where zed = ? order by ? asc", because with prepared statements the question marks can only substitute for VALUES, not schema names. So if you wanted to do something like that it slonik, it would fail. With slonik you CAN do dynamic SQL, that is guaranteed to be safe and checked at compile time with TypeScript, because you can nest SQL tagged templates. That is you can do this:
const colToSortBy = useFoo ? sql`foo` : sql`bar`;
const query = sql`select col from mytable order by ${colToSortBy}`;
In that case slonik will know how to safely "merge" the parent and child parsed SQL.https://github.com/arangodb/arangojs/blob/main/src/aql.ts#L1...
Basically the `aql` template tag returns an object that can also be fed back into it and we also deduplicate arguments to avoid sending redundant data over the wire. There's also an escape hatch via a helper function (`aql.literal`) in cases where you need to insert literals that aren't known at compile time (e.g. you load query filters from a configuration file).
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/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.
Then instantiate like:
``` { [EnumType.First]: ..., [EnumType.Second]: ... } ```
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.A while back I wrote that "Tagged Template Literals Are the Worst Addition to Javascript" 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
You and your team didn't understand how Promise.allSettled behaved?
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.
- 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!!