DEV Community

Masui Masanori
Masui Masanori

Posted on

[TypeScript] Set readonly

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.

const readonlyTexts: readonly string[] = [];
Enter fullscreen mode Exit fullscreen mode

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.

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

const readonlyTexts: readonly string[] = [];
// compile error
readonlyTexts.push('Hello');
Enter fullscreen mode Exit fullscreen mode

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

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();
Enter fullscreen mode Exit fullscreen mode

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

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();
Enter fullscreen mode Exit fullscreen mode

type, interface

I can set properties of type and interface as readonly.

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;
}
Enter fullscreen mode Exit fullscreen mode

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

type MutableType = {
    id: number,
    name: string,
    values: string[]
};
type ImmutableType = {
    readonly [p in keyof MutableType]: MutableType[p]
};
...
Enter fullscreen mode Exit fullscreen mode

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

const immutableValue = {
    id: 0,
    name: 'Example',
    values: [],
} as const;
// compile error
immutableValue.id = 2;
Enter fullscreen mode Exit fullscreen mode

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

So I can reassign like below.

...
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!';
}
...
Enter fullscreen mode Exit fullscreen mode

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.

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

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

...
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' ;
}
...
Enter fullscreen mode Exit fullscreen mode

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

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

Resources

Discussion (0)