4

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?

Yavuz Mester
  • 357
  • 4
  • 11

4 Answers4

3

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.

Meirion Hughes
  • 24,994
  • 12
  • 71
  • 122
  • NB. You can of course write a class that is assignment compatible with the type, which is one interpretation of "implementing the interface". The only limitation is that you cannot tell the compiler that you intended this to be the case (you cannot declare that it implements the interface). But you can implement it in all other senses, thanks to structural typing. – Daniel Earwicker Dec 17 '16 at 23:22
3

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.

Paleo
  • 21,831
  • 4
  • 65
  • 76
2

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.

Nitzan Tomer
  • 155,636
  • 47
  • 315
  • 299
  • is there anyway to make the entire object chain Readonly? For example, what if the `Person` class referenced another object? – NSjonas Sep 16 '17 at 19:25
  • @NSjonas You mean something like in here: https://stackoverflow.com/questions/41879327/deepreadonly-object-typescript/41884828 ? – Nitzan Tomer Sep 16 '17 at 19:31
1

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.

Yaroslav Admin
  • 13,880
  • 6
  • 63
  • 83