
타입스크립트에선 접두사인 readonly 키워드를 사용해서 타입이나 인터페이스, 클래스의 property를 '읽기 전용'으로 지정할 수 있다. readonly property는 초기화할 수 있지만, 초기화 이후엔 const 상수 처럼 할당 표현식의 좌항으로 쓸 수 없다.
readonly는 mutation 과정에서 발생할 수 있는 예측하지 못한 타입 변경을 막기 위해 사용한다. 배열의 접두사로 쓰이면서 코드의 일관성을 유지하는데에 사용한다. 타입에서 쓰고 싶은 경우엔Readonly<>제네릭을 사용해 타입 매개변수로 받는 타입의 모든 property들을readonly로 지정합니다.
function property의 readonly
function foo(config: {
readonly bar: number,
readonly bas: number
}) {
// ..
}
let config = { bar: 123, bas: 123 };
foo(config);
// `config`가 변경되지 않는다고 확신할 수 있음 🌹
type property의 readonly
type Foo = {
readonly bar: number;
readonly bas: number;
}
// 초기화는 오케이
let foo: Foo = { bar: 123, bas: 456 };
// 변경은 안됨
foo.bar = 456; // 오류: 상수나 읽기 전용 속성은 대입(assignment) 표현식의 좌항이 될 수 없음
class property의 readonly
class Foo {
readonly bar = 1; // OK
readonly baz: string;
constructor() { // class의 초기화는 constructor로.
this.baz = "hello"; // OK
}
}
불변성 보장을 위해, 이 외에도 index signatrue도 readonly로 표기해 쓸 수 있다.
아래 처럼 작성하면, Foo가 readonly property들을 가지게 되고 초기화 이후 모든 속성들은 재할당할 수 없다.
interface Foo {
readonly[x: number]: number;
}
let foo: Foo = { 0: 123, 2: 345 };
console.log(foo[0]); // 오케이 (읽기)
foo[0] = 456; // 오류 (변경하기): readonly 임
위와 비슷하게 TypeScript에선 네이티브 배열의 불변성을 위해 ReadonlyArray<T> 인터페이스를 제공하고 있다고 한다.
let foo: ReadonlyArray<number> = [1, 2, 3];
console.log(foo[0]); // 읽기 가능
foo.push(4); // 오류: `push`는 배열의 상태를 바꾸므로 readonly 에러
foo = foo.concat([4]); // 복사본 생성 가능
T> 사용 방법앞서 말했듯이, 제네릭 Readonly<T>는 타입 매개변수로 받은 T의 모든 property들을 readonly로 지정하고 재할당할 수 없게 만든다. 사용 방법은 아래와 같다.
type Foo = {
bar: number;
bas: number;
}
type FooReadonly = Readonly<Foo>;
let foo: Foo = {bar: 123, bas: 456};
let fooReadonly: FooReadonly = {bar: 123, bas: 456};
foo.bar = 456; // 오케이
fooReadonly.bar = 456; // 오류: bar는 readonly임
위에서 살펴보았던 index signature의 readonly를 이용해 제네릭 Readonly<T>를 구현할 수 있다.
type MyReadOnly<T> = {
readonly [P in keyof T]: T[P] // readonly 키워드를 이용한 제네릭 구현
}
index signature와 keyof 키워드로 구현한 타입을 활용하면 아래와 같이, 재할당 불가능한 제네릭을 구현할 수 있다.

