My understanding is that you want to wrap an association into an inert wrapper representing the type.
Immutable version
Then, you should do just that:
TSInitialise[name_String] := DataStructure[<|"Name" -> name, "UpToDate" -> True|>]
Name[DataStructure[assoc_]] := assoc["Name"];
So that
TSTest = TSInitialise["Andrei"]
(* DataStructure[<|"Name" -> "Andrei", "UpToDate" -> True|>] *)
In this version, the data structure carries no mutable state. So, modifications produce a brand new immutable structure. This is easy too, particularly if you don't care about the order of the keys:
ClearAll[dsUpdate, wUpToDate, wName]
dsUpdate[DataStructure[assoc_], k_ -> v_] := DataStructure[Append[assoc, k -> v]];
wUpToDate[ds_DataStructure, updQ_: False] := dsUpdate[ds, "UpToDate" -> updQ];
wName[ds_DataStructure, name_: "Milutin"] := dsUpdate[ds, "Name" -> name];
so that
TSTest = wUpToDate[TSTest]
TSTest = wName[TSTest, "Milutin"]
(* DataStructure[<|"Name" -> "Andrei", "UpToDate" -> False|>] *)
(* DataStructure[<|"UpToDate" -> False, "Name" -> "Milutin"|>] *)
Mutable version
One way to introduce mutability is to make your type container HoldAll, and store a unique symbol inside it. That symbol will be assignable, and will represent the mutable state of your objects:
ClearAll[DataStructureM, TSInitialiseM]
SetAttributes[DataStructureM, HoldAll];
TSInitialiseM[name_String] :=
Module[{assoc = <|"Name" -> name, "UpToDate" -> True|>},
DataStructureM[assoc]
];
Name[DataStructureM[assoc_]] := assoc["Name"];
DataStructureM /: Normal[DataStructureM[assoc_]] := assoc;
Modifications are also easy to implement in this approach. They will now modify the mutable state of the object passed to them.
ClearAll[dsUpdate, wUpToDate, wName]
dsUpdate[ds : DataStructureM[assoc_], k_ -> v_] :=
Module[{},
assoc[k] = v;
ds
];
wUpToDate[ds_DataStructureM, updQ_: False] := dsUpdate[ds, "UpToDate" -> updQ];
wName[ds_DataStructureM, name_: "Milutin"] := dsUpdate[ds, "Name" -> name];
So, in contrast to the immutable case, they will not produce the new immutable objects, but will change the state of the original one:
(testM = TSInitialiseM["Andrei"]) // Normal
wUpToDate[testM];
testM // Normal
wName[testM];
testM // Normal
(*
<|"Name" -> "Andrei", "UpToDate" -> True|>
<|"Name" -> "Andrei", "UpToDate" -> False|>
<|"Name" -> "Milutin", "UpToDate" -> False|>
*)
So, the above output represents the state of the same object in 3 different points in time, while for the immutable case, we had produced 3 different immutable objects.
Which one to use depends on the problem you are solving. You should just keep in mind that using mutable objects in Mathematica should always be a conscious choice, since doing so you lose many advantages of immutability (particularly in the context of Mathematica which has been optimized for this), and get all the problems associated with manipuations with mutable state. There are cases where it is worth it, but I wouldn't say they are typical.