[TypeScript] Set readonly
Tags: typescript
Published: 2021-10-09 18:27:31

## Intro
I have only added "readonly" into Array type before.
But I found that it can be used for other things.

So I will try "readonly" in this time.

#### Environments
* Node.js ver.16.10.0
* TypeScript ver.4.4.3

## readonly array
I can set array as readonly.

```ts
const readonlyTexts: readonly string[] = [];
```
I can use it as declaration, arguments, return value, and so on.
Except I can't reassign value, it has a little differences with default array.

```ts
const mutableTexts: string[] = [];
// OK
mutableTexts.push('Hello');

const readonlyTexts: readonly string[] = [];
// compile error
readonlyTexts.push('Hello');
```

In TypeScript world, readonly array and default array are separated.
So I can't use a readonly array value as a default array value.

```ts
function init(): void {
    const mutableTexts: string[] = [];
    // OK
    mutableTexts.push('Hello');
    // OK
    editArrayValue(mutableTexts);
    
    const readonlyTexts: readonly string[] = [];
    // compile error
    editArrayValue(readonlyTexts);    
}
function editArrayValue(values: string[]): void {
    values.push('World');
}
init();
```

But I can use a default array value as a readonly array value.

```ts
function init(): void {
    const mutableTexts: string[] = [];
...
    // OK
    logArrayValue(mutableTexts);
    
    const readonlyTexts: readonly string[] = [];
    // OK
    logArrayValue(readonlyTexts); 
}
...
function logArrayValue(values: readonly string[]): void {
    for(const v of values) {
        console.log(v);        
    }
}
init();
```

## type, interface
I can set properties of type and interface as readonly.

```ts
type MutableType = {
    id: number,
    name: string,
    values: string[]
};
type ImmutableType = {
    readonly id: number,
    readonly name: string,
    readonly values: string[]
};
function init(): void {
    const mutableValue: MutableType = {
        id: 0,
        name: 'Example',
        values: [],
    };
    // OK
    mutableValue.id = 2;
    
    const immutableValue: ImmutableType = {
        id: 0,
        name: 'Example',
        values: [],
    };
    // compile error
    immutableValue.id = 2;
}
```

In this sample, I can create the "ImmutableType" in more simple way.

```ts
type MutableType = {
    id: number,
    name: string,
    values: string[]
};
type ImmutableType = {
    readonly [p in keyof MutableType]: MutableType[p]
};
...
```

I can set the object properties types as readonly by "as const".

```ts
const immutableValue = {
    id: 0,
    name: 'Example',
    values: [],
} as const;
// compile error
immutableValue.id = 2;
```

* [TypeScript Documentation - Mapped Types](https://www.typescriptlang.org/docs/handbook/2/mapped-types.html)
* [TypeScript Documentation - Keyof Type Operator](https://www.typescriptlang.org/docs/handbook/2/keyof-types.html)

It only for avoiding reassign value.
Because the readonly value is as same as the default value.

So I can reassign like below.

```ts
...
function init(): void {
...
    const immutableValue: ImmutableType = {
        id: 0,
        name: 'Example',
        values: [],
    };
    editValue(immutableValue);
    // name becomes "Hello world!'
    console.log(immutableValue);
}
function editValue(value: MutableType): void {
    value.name = 'Hello world!';
}
...
```

So I think setting arguments as readonly is more important than setting return values as readonly.

## ReadOnly
I can use "readonly" keyword into only array for using as arguments, return value, property types.
But as I wrote above, I should set readonly for all types if I could.

So I use "Readonly" for do it.

```ts
...
function editValue(value: Readonly<MutableType>): void {
    // compile error
    value.name = 'Hello world!';
}
...
```

One important thing is than it only works for "object" type.

```ts
...
function init(): void {    

    const immutableValue: ImmutableType = {
        id: 0,
        name: 'Example',
        values: [],
    };
    editName(immutableValue.name);
    // name value is "Example"
    console.log(immutableValue);
}
function editName(name: Readonly<string>): void {
    // OK
    name = 'Hello world' ;
}
...
```

So I must copy the value as a new const variable.

```ts
...
function editName(name: Readonly<string>): void {
    const targetName = name;
    // compile error
    targetName = 'Hello world' ;
}
...
```

## Resources
* [TypeScript Documentation - Utility Types](https://www.typescriptlang.org/docs/handbook/utility-types.html#readonlytype)
* [Readonly - TypeScript Deep Dive](https://basarat.gitbook.io/typescript/type-system/readonly#readonly-1)
* [TypeScriptのreadonlyプロパティを使いこなす - Qiita](https://qiita.com/uhyo/items/0fd033ff1aed9b4b32dd)