To achieve this, we can utilize utility types or mapped types directly. Here is an example:
type MakeSomePropsReadonly<A, K extends keyof A> = Readonly<Pick<A, K>> & Omit<A, K>
type Original = {
a: string,
b: number,
c: boolean
}
type Example = MakeSomePropsReadonly<Original, 'a'>
const example: Example = {
a: 'a',
b: 1,
c: true
}
example.a = 'b'; // error property is readonly
example.b = 2; // not readonly, so it's okay
The
Readonly<Pick<A, K>> & Omit<A, K>
type does the following:
Readonly<Pick<A, K>>
- creates a type with only keys picked by K
that are readonly
& Omit<A, K>
- intersect the previous type with the second part to create an object with fields different from key K
This results in combining one type with all readonly properties and another with writable ones.
We can also approach it differently by having a readonly interface and selecting which fields are writable:
type MakeSomePropsWritable<A, K extends keyof A> = {
-readonly [Key in K]: A[Key]
} & Omit<A, K>
type Original = {
readonly a: string,
readonly b: number,
readonly c: boolean
}
type Example = MakeSomePropsWritable<Original, 'a'>
const example: Example = {
a: 'a',
b: 1,
c: true
}
example.a = 'b'; // now allowed since it is set as writable
example.b = 2; // error as expected
No significant difference in this implementation apart from:
-readonly [Key in K]: A[Key]
- removing readonly from specified fields using the -
prefix