Yavuz Mester
Yavuz Mester

Reputation: 367

Typescript: extending an interface and redeclaring the existing fields as readonly

Let's say we have an interface like this:

interface Person {
  name: string;
  age: number;
}

I want to call Readonly and create a readonly version of the interface, e.g.

interface PersonReadonly extends Readonly<Person> {}

which will be equivalent to writing

interface PersonReadonly {
  readonly name: string;
  readonly age: number;
}

Can we write such a Readonly generic interface, or is it written already?

Upvotes: 4

Views: 5941

Answers (4)

Paleo
Paleo

Reputation: 23752

You can do:

type PersonReadonly = Readonly<Person>

But it is not an interface. For example, you can't add a new member somewhere else.

Edit from May, 2017: Since TS 2.2 (February, 2017), interfaces can be derived from types.

Upvotes: 3

Meirion Hughes
Meirion Hughes

Reputation: 26438

To answer you explicit question: You technically can't do what you want yet.

Type Mapping will create a Type not an interface -- Readonly<T> is a built in type mapper that will return a Type. You cannot implement or extend Type aliases, only interface / class.

thus:

interface PersonReadonly extends Readonly<Person> {}

is invalid until support for implementing types are done, if ever.

That doesn't stop you passing around the Type though; and you can use union on types to build up more complex types too. thus you can do:

type PersonWithState = Readonly<Person> & { state: string }

let person = <Person>{ name: "John", age: 20 };
let personState = <PersonWithState>{ ...person, state: "online" };

personState.age = "30"; // error;
personState.state = "offline"; // OK.

but you cannot have a class implement, or interface extend, PersonWithState - yet.

Upvotes: 3

Nitzan Tomer
Nitzan Tomer

Reputation: 164347

In playground the Readonly type is defined so you can do:

interface Person {
    name: string;
    age: number;
}

let a: Readonly<Person> = {
    name: "name",
    age: 3
};

a.age = 5; // Error: Cannot assign to 'age' because it is a constant or a read-only property

(code in playground)

If the type is defined in your environment then you can simply add it:

type Readonly<T> = {
    readonly [P in keyof T]: T[P];
}

But it requires you to have typescript 2.1 and above.
If you don't have it, it probably means that your typescript version is below 2.1, otherwise you can just use it.

Upvotes: 2

Yaroslav Admin
Yaroslav Admin

Reputation: 14535

The generic interface for making all fields of the instance readonly is available as of TypeScript 2.1.

It's called exactly Readonly<T>, so you can use it like this:

let person: Readonly<Person> = { name: 'John', age: 30 };
person.age = 31; // gives error

It's impossible to implement generic readonly type before TypeScript 2.1.

Upvotes: 1

Related Questions