I like programming languages where variables are immutable by default. For example, in rust, let declares an immutable variable and let mut declares the variable. I’ve long wanted this in other languages like TypeScript, which are mutable by default – the opposite of what I want!
I wondered: Is it possible to make TypeScript values immutable by default?
My goal was to do it completely with TypeScript, without changing TypeScript. This meant no lint rules or other tools. I chose this because I wanted this solution to be as “pure” as possible…and also look more fun.
I spent an evening trying to do this. I failed but made progress! I made tables and RecordIt’s immutable by default, but I couldn’t get it to work for regular objects. If you’ve figured out how to do this perfectly, please contact me—I’d love to know!
Step 1: Clear the Built-in Libraries
TypeScript has built-in type definitions for the JavaScript API. Array And Date And StringIf you ever changed target Or lib Options in your TSConfig, you change which of these definitions you include. For example, if you are targeting a new runtime you can add the “ES2024” library.
My goal was to swap out the built-in libraries with immutable-by-default replacements.
The first step was to stop using any built-in libraries. i set noLib Flag in my TSConfig, like this:
{
"compilerOptions": {
"noLib": true
}
}
Then I wrote a very simple script and put it in test.ts,
console.log("Hello world!");
when i ran tscThis gave a bunch of errors:
Cannot find global type 'Array'.
Cannot find global type 'Boolean'.
Cannot find global type 'Function'.
Cannot find global type 'IArguments'.
Cannot find global type 'Number'.
Cannot find global type 'Object'.
Cannot find global type 'RegExp'.
Cannot find global type 'String'.
Progress! I successfully wiped out any default TypeScript libraries that I could tell because it couldn’t find such key types. String Or Boolean,
It’s time to write the replacement.
Step 2: A skeleton standard library
This project was a prototype. So, I started with a minimal solution that would type-check. I didn’t need this to be good!
i made lib.d.ts And put the following inside:
// In lib.d.ts:
declare var console: any;
interface Boolean {}
interface Function {}
interface IArguments {}
interface Number {}
interface RegExp {}
interface String {}
interface Object {}
// TODO: We'll update this soon.
interface Array {}
Now, when I ran tscI did not find any errors! I have defined all the built-in types needed for TypeScript and a dummy console object.
As you can see, this solution is impractical for production. First of all, none of these interfaces have any properties! "foo".toUpperCase() For example is not defined. That’s okay because it’s only a prototype. A production-ready version will need to define all those things – tedious, but should be straightforward.
Step 3: Making arrays immutable
I decided to tackle it with a test-driven development style. I will write some code which I want to check by typing it and see it fail To type-check, then fix it.
I updated test.ts To include the following:
// In test.ts:
const arr = [1, 2, 3];
// Non-mutation should be allowed.
console.log(arr[1]);
console.log(arr.map((n) => n + 1));
// @ts-expect-error Mutation should not be allowed.
arr[0] = 9;
// @ts-expect-error Mutation should not be allowed.
arr.push(4);
It tests three things:
- It is possible to create arrays with array literals.
- Non-transformative operations, such as
arr[1]Andarr.map()are allowed. - Operations that change the array, like
arr[1] = 9Are rejected.
when i ran tscI saw two errors:
arr[0] = 9Permission is given. there is an unused@ts-expect-errorThere.arr.mapDoesn’t exist.
so i updated it Array type in lib.d.ts with the following:
// In lib.d.ts:
interface Array {
readonly [n: number]: T;
map(
callbackfn: (value: T, index: number, array: readonly T[]) => U,
thisArg?: any
): U[];
}
property accessor-the readonly [n: number]: T line—tells TypeScript that you can access array properties by numeric index, but that they are read-only. he should make arr[1] possible but arr[1] = 9 impossible.
map The method definition is copied from the TypeScript source code without any changes (apart from some auto-formatting). This will make it possible to make calls arr.map(),
Note that I did No define pushWe should not be calling this on an immutable array!
i ran tsc Again and…success! no errors! Now we have immutable tables!
At this stage, I have shown that It is possible to configure TypeScript to make all arrays immutable without any additional annotationsno need for readonly string[] Or ReadonlyArrayIn other words, we have some immutability by default.
This code, like everything else in this post, is simple. There are many other array methods, such as filter() And join() And forEach()If this were to be made for production, I would make sure to define all read-only array methods.
But for now, I was ready to move on to mutable arrays.
Step 4: Variable Arrays
I prefer immutability, but I want to be able to define a mutable array sometimes. So I created another test case:
// In test.ts:
const arr = [1, 2, 3] as MutableArray;
arr[0] = 9;
arr.push(4);
Note that making the array mutable requires a little extra work. In other words, it is not the default.
TypeScript complained that it could not be found MutableArraySo I defined it:
// In lib.d.ts:
interface MutableArray extends Array {
[n: number]: T;
push(...items: T[]): number;
}
And then, type-check passed!
Now, I had mutable and immutable arrays, with immutability being the default. Again, this is simple, but good enough for a proof of concept!
It was exciting for me. At least for arrays it was possible to configure TypeScript to make them immutable by default. I didn’t need to fork the language or use any other tools.
Can I make more things immutable?
Step 5: Same for Record
I wanted to see if I could go beyond arrays. my next goal was Record Type, which is a TypeScript utility type. So I defined another pair of test cases similar to the ones I created for arrays:
// In test.ts:
// Immutable records
const obj1: Record = { foo: "bar" };
console.log(obj1.foo);
// @ts-expect-error Mutation should not be allowed.
obj1.foo = "baz";
// Mutable records
const obj2: MutableRecord = { foo: "bar" };
obj2.foo = "baz";
TypeScript complained that it could not be found Record Or MutableRecordThere was also a complaint about unused @ts-expect-errorWhich meant that mutation was allowed.
I rolled up my sleeves and fixed those errors as follows:
// In lib.d.ts:
declare type PropertyKey = string | number | symbol;
type Record = {
readonly [key in KeyT]: ValueT;
};
type MutableRecord = {
[key in KeyT]: ValueT;
};
now we have RecordWhich is an immutable key-value pair, and also has mutable versions. Just like arrays!
You can imagine extending this idea to other built-in types, like Set And MapI think it would be very easy to do it the same way I did the array and record, I’ll leave this as an exercise for the reader,
Fail Step 6: Plain Objects
My final test was to make regular objects (not records or arrays) immutable. Unfortunately for me, I couldn’t figure it out.
Here is the test case I wrote:
// In test.ts:
const obj = { foo: "bar" };
console.log(obj.foo);
// @ts-expect-error Mutation should not be allowed.
obj.foo = "baz";
It stunned me. No matter what I did, I could not write a type that would reject this mutation. I tried to modify it Object Typed every way I could think of, but failed!
There are ways to annotate obj To make it immutable, but that’s not in the spirit of my goal. I want it to be immutable by default!
Alas, this is where I gave up.
Can you figure it out?
I wanted to make TypeScript immutable by default. I was able to do this with arrays, Records, and other types such as Map And SetUnfortunately, I couldn’t get it to work for general object definitions obj = { foo: "bar" },
There is probably a way to enforce this with Lint rules, either by not allowing mutation operations or by requiring Readonly Comments everywhere. I’d like to see what he looks like.
If You Learn how to make TypeScript immutable by default without any other equipmentI’d love to know, and will update my post. I hope my failed attempt will make someone else successful.
Again, if you figure this out, or have any other ideas, please contact me.
Услуги по настройке https://sysadmin.guru и администрированию серверов и компьютеров. Установка систем, настройка сетей, обслуживание серверной инфраструктуры, защита данных и техническая поддержка. Помогаем обеспечить стабильную работу IT-систем.
A website with unblocked games for free online play. Popular browser games, arcades, platformers, racing games, and puzzles are available with no downloads or restrictions on any device.
купить женское кольцо кольца для помолвки с бриллиантами
Accurate weather forecast https://www.the-weather-in-kotor.com for today, tomorrow, and next week. Temperature, precipitation, wind, and humidity are all included. Follow the weather in Kotor and get up-to-date weather data online.
Если вам нравится стиль провайдера Hacksaw Gaming – резкие бонуски, высокая динамика и слоты, которые часто держат в напряжении до последнего спина – загляните в наш Telegram. Мы ведём канал именно про Hacksaw: публикуем подборки лучших тайтлов, разбираем фичи (покупка бонуса, модификаторы, этапы бонус-раундов), отмечаем, какие игры больше “на разнос”, а какие спокойнее по темпу, и делимся новинками, как только они появляются. Удобно, если хотите быть в теме и быстро выбирать, во что сыграть сегодня.