This is more of an open Question on how to deal with functions that have type altering side effects in TypeScript
. I know and strongly agree with the notion, that functions should have as few side effects as possible, if any.
But sometimes, it is desirable to change an object (and it's type) in-place instead of creating a new copy of it with another static type. Reasons I have come across most frequently are readability, efficiency or reducing line count.
Since my original example was too convoluted and over-complicated a (hopefully) very basic example here:
type KeyList = 'list' | 'of' | 'some' | 'keys';
// Original type (e.g. loaded from a JSON file)
interface Mappable {
source: { [K in KeyList]: SomeNestedObject },
sourceOrder: KeyList[];
}
// Mapped Type (mapped for easier access)
interface Mapped {
source: { [K in KeyList]: SomeNestedObject },
sourceOrder: SomeDeepObject[];
}
// What I have to do to keep suggestions and strict types all the way
const json: Mappable = JSON.parse(data); // ignoring validation for now
const mapped: Mapped = toMappedData(json);
// What I would like to to
const mapped: Mappable = JSON.parse(data);
mapData(mapped); // mapped is now of type Mapped
Reasons, why I would like to mutate both object properties and it's type in-place could be:
- The
json
object is very large and it would be counterproductive to have 2 copies of it in memory - It is very cumbersome to create a deep copy of the
json
object into themapped
object
I don't believe the code under "What I would like to do" is very readable, regardless of it not working. What I'm looking for is a clean and type safe way to navigate this issue. Or, alternativley, suggestions or ideas to extend typescript's functionality towards solving this issue.
Any suggestions, ideas and comments on this are greatly appreciated! Maybe I'm just in too deep with this and can't see the really simple solution.
KeyList
and what isSomeDeepObject
? Are you saying that they can be converted into each other with an identity function? – ValvuletoMappedData
function that changes the value of.sourceOrder
? – Valvuleconst mapped: Mappable = JSON.parse(data); setTimeout(() => 'What type is mapped now?', 100); mapData(mapped);
would be impossible for typescript to check. – Plaque