Recently, I noticed a Twitter thread from Rob Palmer in which he described some performance problems that were caused by the use of type alias declarations in TypeScript.
Specifically, the use of a type alias declaration effected a much larger .d.ts
output:
Yesterday I shrank a TypeScript declaration from 700KB to 7KB by changing one line of code 🔥
In the thread, Rob points out that the reason this happens is because type alias declarations can be inlined, whereas interfaces are always referenced by name.
Let’s have a look at some code that demonstrates this inlining behaviour.
Here’s a TypeScript playground snippet in which a type alias is used to declare the callback…
When solving a problem that involved a frozen object, I learnt a bit more about the details of JavaScript object property access and some of those details are kinda interesting.
Recently, I replaced an ESLint rule that was specific to arrays — no-array-foreach
— with a more general rule — no-foreach
— that also effected failures for the Set
, Map
and NodeList
types.
The no-foreach
rule supports a types
option — so the that the types for which it is enforced can be configured by the developer — and I planned to leverage that by deprecating and re-implementing no-array-foreach
to use no-foreach
internally. …
RxJS — and Rx in general — offers a bunch of guarantees that make it possible to compose observable chains. Specifically, it’s guaranteed that:
error
or complete
notification; andThere is more information about this in the Observable Contract.
The guarantees allow for the declarative composition of observable chains: developers can focus on composing behaviours rather than keeping track of state. Without the guarantees, developers would be checking for errored or completed sources and composition would be more difficult.
A quirk that stems from these guarantees is that it’s possible for an error channel to be closed. For example, if an error occurs when an observable is being torn down, it’s not possible for that error to be reported to the observer — the observer will have already received a complete
or error
notification or will have already been explicitly unsubscribed. In RxJS, observers in this state are referred to (internally) as stopped. …
I thought I’d write up a short post to explain a TypeScript problem that took up far too much of my time.
The TL;DR is that if you have overload signatures for a function that takes a variable number of (initial) arguments, the ordering of the signatures matters in a way that is not obvious. And the ‘simplest’ signature should be placed first.
The types I was working with were in RxJS, but to keep things general let’s use an example that combines a bunch of arrays in some way, like this:
The publish
and publishReplay
operators can be called in two ways: either with or without a selector function. Let’s look at the differences and at why you should almost always pass a selector.
When publish
is called without a selector, it returns a particular type of observable: a ConnectableObservable
.
A ConnectableObservable
is a little unusual. It does not connect — i.e. subscribe — to its source when a subscriber subscribes. Instead, a ConnectableObservable
subscribes to its source when its connect
method is called.
This behaviour can be used to control the order in which subscriptions occur and to compose observables before a subscription is made to the source. …
A while ago, I wrote a linting rule to highlight an error that I’d made on a few occasions. Let’s look at the error — and its variations — and then at the rule that prevents it.
If you are used to working with fluent APIs — perhaps with D3 or lodash — it feels natural to write Array
-based code like this:
const developers = employees
.filter(e => e.role === "developer")
.map(e => e.name)
.sort();
Which is fine. There’s no error in that code.
However, in situations in which you don’t need to filter or map, you might write something like…
Introducing unintentional dependencies on rxjs-compat
is something that I see developers doing every now and then. Let’s have a look at rxjs-compat
to see what it is, how it works and how depending upon it can be avoided.
If you’re only interested in avoiding the dependency, skip to the TL;DR at the bottom of the article.
In RxJS version 6, breaking changes made the library simpler:
Those changes made the library easier to maintain, document and explain, but they created a burden for developers with large RxJS-version-5 codebases: pretty much all RxJS-related code would need to be modified. …
In RxJS version 6.4.0, a change was made to the shareReplay
operator. Let’s look at why the operator needed to be changed, what was changed and how the change can be used to avoid surprises — and bugs.
If you’re only interested in the change, skip to the TL;DR at the bottom of the article.
The shareReplay
operator was introduced in version 5.4.0.
Like the share
operator, shareReplay
returns a hot, reference-counted observable, but replays the specified number of next
notifications. …
When pipeable operators were introduced in RxJS version 5.5, writing user-land operators became much simpler.
A pipeable operator is a higher-order function: a function that returns another function. And the function that is returned takes an observable and returns an observable. So, to create an operator, you don’t have to subclass Operator
and Subscriber
. You just write a function.
Simple.
However, there are situations in which you need to take some extra care. In particular, you need to be careful whenever your operator stores internal state.
Let’s look at an example: a debug
operator that logs received values and their indices to the console. …
I often see code that looks a little like this:
Which seems fine.
Well, it is fine — as long as the extractSomeProperty
and handleError
methods do not depend upon the this
context in their implementations.
When unbound methods are passed to RxJS, they will be invoked with an unexpected context for this
. If the method implementations don’t use this
, they will behave as you would expect.
However, there are a number of reasons why, as a general rule, you might want to avoid passing unbound methods:
this
in its implementation. Maybe you’ll have a test that picks it up; maybe you won’t. …About