100

In TypeScript, is it possible to remove the readonly modifier from a type?

For example:

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

Usage:

interface Foo {
    readonly bar: boolean;
}

let baz: Writeable<Foo>;

baz.bar = true;

Is it possible to add a modifier to the type to make all the properties writeable?

2
  • 2
    Official TypeScript issue about that feature request: github.com/microsoft/TypeScript/issues/24509 Commented Jan 21, 2022 at 15:57
  • If you came here because you had an array with as const and later you wanted to sort, TS is not letting you do a bad thing, because sort changes the existing array, which would make it not const. Splat the array, then you can use sort on it. TS saves you from a bug later down the road. Commented Oct 28 at 20:16

3 Answers 3

190

Update

Since typescript 2.8 there's a new way to do it:

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

If you need your type to be writeable recursively, then:

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

These type definitions are called mapped types


Old Answer

There's a way:

type Writeable<T extends { [x: string]: any }, K extends string> = {
    [P in K]: T[P];
}

(code in playground)

But you can go the opposite way and it will make things much easier:

interface Foo {
    bar: boolean;
}

type ReadonlyFoo = Readonly<Foo>;

let baz: Foo;
baz.bar = true; // fine

(baz as ReadonlyFoo).bar = true; // error

(code in playground)

Sign up to request clarification or add additional context in comments.

7 Comments

Awesome. I wonder why this isn't a standard type? typescriptlang.org/docs/handbook/utility-types.html
type Writeable<T> = { -readonly [P in keyof T]: T[P] }; works great even for classes used as T because when using the class keyword in TypeScript, you are actually creating TypeScript interface containing all the instance methods and properties of the class! (fyi the second thing created with the TypeScript class keyword is a JavaScript variable with a different (anonymous) constructor function type.)
N.B. this particular implementation fails in many edge cases - I've found the DeepWritable from ts-essentials has worked well though and is basically a more complete version of the one presented in this answer (handles maps, functions, sets, promises etc).
FYI: type Writeable<T> = { -readonly [P in keyof T]: T[P] }; works flawlessly for readonly tuple types (usually created from const assertions on array literals) even though it looks like it's geared towards objects only.
The DeepWriteable saved my day, thank you very much!
|
0

To add to accepted answer, if you just wanna remove one or a small number of readonly props from a type you can do this (playground):

interface Foo {
    readonly bar: boolean;
}
type WriteableFoo = Foo & { bar: Foo['bar'] };
const baz: Foo = { bar: true };
// baz.bar = true; // Cannot assign to 'bar' because it is a read-only property.(2540)
(baz as WriteableFoo).bar = false;

Works in v4 and 5, but strangely, doesn't work in v3 (and presumably earlier).

Comments

0

Sorry to point out something obvious (it's perhaps not what you're looking for), but if you know what the original type is going to be you can just cast it back to the original type. This doesn't work if you're looking for a generic way to possibly remove the readonly-ness of a possibly readonly type or do so recursively...

type Foo = { x: number }

const y: Readonly<Foo> = {x: 3};

// y.x = 4 // error

(y as Foo).x = 4 // okay
/// JSDOC style

// @ts-check

/** 
 * @typedef {Object} Foo
 * @property {number} x
 */

/** @type {Readonly<Foo>} */

const y = { x: 3 };

// y.x = 4 // error

/** @type {Foo} */(y).x = 3 // okay

1 Comment

The original type itself could be declared with readonly fields, in which case there's no existing non-readonly type to cast to. That's one of the main use cases where the generic Writable<T> approach comes in handy, in my experience.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.