Repository: dynamoose/dynamoose

Issue:
[BUG] Not able to use update with Model.update $ADD with typescript
<!-- Not filling out ALL of the relevant fields in this issue will cause your issue to be closed -->

### Summary:
Not able to use update with $ADD with typescript


### Code sample:
`await clientModel.update({'id': clientId}, {"$ADD": {"facilities": [facilityId]}})`
#### Schema
```
`const clientSchema = new Schema(
  {
    id: { hashKey: true, type: String, default: v4() },
    name: String,
    phone: { type: String },
    email: { type: String },
    // ownedQueues: [{ type: Types.ObjectId, ref: 'QueueTemplate' }],
    // controlledQueues: [{ type: Types.ObjectId, ref: 'QueueTemplate' }],
    facilities: [String]
  },
  { timestamps: true }
);`
// Code here
```
Interface:
`export interface IClient extends Document {
  id: string;
  name: string;
  phone: string;
  email: string;
  ownedQueues?: string[];
  controlledQueues?: string[];
  facilities?: string[];
}`


### Current output and behavior (including stack trace):
`No overload matches this call.
  Overload 1 of 10, '(obj: Partial<IClient>, callback: CallbackType<IClient, AWSError>): void', gave the following error.
    Argument of type '{ $ADD: { facilities: string[]; }; }' is not assignable to parameter of type 'CallbackType<IClient, AWSError>'.
      Object literal may only specify known properties, and '"$ADD"' does not exist in type 'CallbackType<IClient, AWSError>'.
  Overload 2 of 10, '(keyObj: ObjectType, updateObj: Partial<IClient>): Promise<IClient>', gave the following error.
    Argument of type '{ $ADD: { facilities: string[]; }; }' is not assignable to parameter of type 'Partial<IClient>'.
      Object literal may only specify known properties, and '"$ADD"' does not exist in type 'Partial<IClient>'`



### Expected output and behavior:

update need to happen


### Environment:

Operating System:
Operating System Version:
Node.js version (`node -v`): v12.13.0
NPM version: (`npm -v`):6.12.0
Dynamoose version:2.7.3


### Other information (if applicable):




### Other:
- 

- [x] I have read through the Dynamoose documentation before posting this issue

- [x] I have searched through the GitHub issues (including closed issues) and pull requests to ensure this issue has not already been raised before
- [x] I have searched the internet and Stack Overflow to ensure this issue hasn't been raised or answered before
- [x] I have tested the code provided and am confident it doesn't work as intended
- [x] I have filled out all fields above
- [x] I am running the latest version of Dynamoose


Discussion:
@marcelomagalhaes-ilia: Any update on this?@fishcharlie: There are currently no updates on this issue. Pull requests are always welcome. Additionally, this project uses the reaction voting system to prioritize issues. If you are interested in this issue please 👍 the original issue to indicate your interest in this issue.

Any updates to this issue will be posted here, so feel free to subscribe to this issue to receive updates on its progress.@RationalG: This issue makes Dynamoose unusable if atomic counters are needed. +1@lmanerich: A workaround is to create transaction for this update:

```ts
await dynamoose.transaction([
  clientModel.transaction.update({'id': clientId}, {"$ADD": {"facilities": [facilityId]}})
]);
```@kevinlbatchelor: The documentation shows that you can do atomic updates.

From the docs

`await User.update({"id": 1}, {"$SET": {"name": "Bob"}, "$ADD": {"age": 1}});`

Bur reading through the code I don't see anything to handle this. I think this is a bad docs?@fishcharlie: > Bur reading through the code I don't see anything to handle this. I think this is a bad docs?

Nope. DynamoDB handles this for us. No need to do anything on our end. But the docs are accurate.@fishcharlie: I can't seem to reproduce this issue. I just added another type test to the library. Maybe a recent version of Dynamoose has fixed this. If anyone is able to create a reproducible example I'd be happy to look at this further. Otherwise this issue will be closed soon.

See my added test in this commit: c4f5fbf25bc47f5b88b1dbf946a6a4198323b5f8@github-actions: This issue is stale because it has been open 7 days with no activity. Remove stale label or comment or this will be closed in 3 days.

Solution patch:
diff --git a/PENDING_CHANGELOG.md b/PENDING_CHANGELOG.md
new file mode 100644
index 00000000..9af480be
--- /dev/null
+++ b/PENDING_CHANGELOG.md
@@ -0,0 +1,7 @@
+# Dynamoose Changelog
+
+---
+
+### Bug Fixes
+
+- Fixed inputs types for Mode.update(), allowing $SET, $ADD, $DELETE and $REMOVE
\ No newline at end of file
diff --git a/packages/dynamoose/lib/Model/index.ts b/packages/dynamoose/lib/Model/index.ts
index 5d6943dd..a0172c5a 100644
--- a/packages/dynamoose/lib/Model/index.ts
+++ b/packages/dynamoose/lib/Model/index.ts
@@ -20,6 +20,13 @@ import {Instance} from "../Instance";
 import returnModel from "../utils/dynamoose/returnModel";
 const {internalProperties} = Internal.General;
 
+type UpdatePartial<T> =
+  | Partial<T>
+  | { $SET: Partial<T> }
+  | { $ADD: Partial<T> }
+  | { $REMOVE: Partial<T> }
+  | { $DELETE: Partial<T> };
+
 // Transactions
 type GetTransactionResult = Promise<GetTransactionInput>;
 type CreateTransactionResult = Promise<CreateTransactionInput>;
@@ -660,25 +667,25 @@ export class Model<T extends ItemCarrier = AnyItem> extends InternalPropertiesCl
 	}
 
 	// Update
-	update (obj: Partial<T>): Promise<T>;
-	update (obj: Partial<T>, callback: CallbackType<T, any>): void;
-	update (keyObj: InputKey, updateObj: Partial<T>): Promise<T>;
-	update (keyObj: InputKey, updateObj: Partial<T>, callback: CallbackType<T, any>): void;
-	update (keyObj: InputKey, updateObj: Partial<T>, settings: ModelUpdateSettings & {"return": "request"}): Promise<DynamoDB.UpdateItemInput>;
-	update (keyObj: InputKey, updateObj: Partial<T>, settings: ModelUpdateSettings & {"return": "request"}, callback: CallbackType<DynamoDB.UpdateItemInput, any>): void;
-	update (keyObj: InputKey, updateObj: Partial<T>, settings: ModelUpdateSettings): Promise<T>;
-	update (keyObj: InputKey, updateObj: Partial<T>, settings: ModelUpdateSettings, callback: CallbackType<T, any>): void;
-	update (keyObj: InputKey, updateObj: Partial<T>, settings: ModelUpdateSettings & {"return": "document"}): Promise<T>;
-	update (keyObj: InputKey, updateObj: Partial<T>, settings: ModelUpdateSettings & {"return": "document"}, callback: CallbackType<T, any>): void;
-	update (keyObj: ObjectType, updateObj: Partial<T>): Promise<T>;
-	update (keyObj: ObjectType, updateObj: Partial<T>, callback: CallbackType<T, any>): void;
-	update (keyObj: ObjectType, updateObj: Partial<T>, settings: ModelUpdateSettings & {"return": "request"}): Promise<DynamoDB.UpdateItemInput>;
-	update (keyObj: ObjectType, updateObj: Partial<T>, settings: ModelUpdateSettings & {"return": "request"}, callback: CallbackType<DynamoDB.UpdateItemInput, any>): void;
-	update (keyObj: ObjectType, updateObj: Partial<T>, settings: ModelUpdateSettings): Promise<T>;
-	update (keyObj: ObjectType, updateObj: Partial<T>, settings: ModelUpdateSettings, callback: CallbackType<T, any>): void;
-	update (keyObj: ObjectType, updateObj: Partial<T>, settings: ModelUpdateSettings & {"return": "item"}): Promise<T>;
-	update (keyObj: ObjectType, updateObj: Partial<T>, settings: ModelUpdateSettings & {"return": "item"}, callback: CallbackType<T, any>): void;
-	update (keyObj: InputKey | ObjectType, updateObj?: Partial<T> | CallbackType<T, any> | CallbackType<DynamoDB.UpdateItemInput, any>, settings?: ModelUpdateSettings | CallbackType<T, any> | CallbackType<DynamoDB.UpdateItemInput, any>, callback?: CallbackType<T, any> | CallbackType<DynamoDB.UpdateItemInput, any>): void | Promise<T> | Promise<DynamoDB.UpdateItemInput> {
+	update (obj: UpdatePartial<T>): Promise<T>;
+	update (obj: UpdatePartial<T>, callback: CallbackType<T, any>): void;
+	update (keyObj: InputKey, updateObj: UpdatePartial<T>): Promise<T>;
+	update (keyObj: InputKey, updateObj: UpdatePartial<T>, callback: CallbackType<T, any>): void;
+	update (keyObj: InputKey, updateObj: UpdatePartial<T>, settings: ModelUpdateSettings & {"return": "request"}): Promise<DynamoDB.UpdateItemInput>;
+	update (keyObj: InputKey, updateObj: UpdatePartial<T>, settings: ModelUpdateSettings & {"return": "request"}, callback: CallbackType<DynamoDB.UpdateItemInput, any>): void;
+	update (keyObj: InputKey, updateObj: UpdatePartial<T>, settings: ModelUpdateSettings): Promise<T>;
+	update (keyObj: InputKey, updateObj: UpdatePartial<T>, settings: ModelUpdateSettings, callback: CallbackType<T, any>): void;
+	update (keyObj: InputKey, updateObj: UpdatePartial<T>, settings: ModelUpdateSettings & {"return": "document"}): Promise<T>;
+	update (keyObj: InputKey, updateObj: UpdatePartial<T>, settings: ModelUpdateSettings & {"return": "document"}, callback: CallbackType<T, any>): void;
+	update (keyObj: ObjectType, updateObj: UpdatePartial<T>): Promise<T>;
+	update (keyObj: ObjectType, updateObj: UpdatePartial<T>, callback: CallbackType<T, any>): void;
+	update (keyObj: ObjectType, updateObj: UpdatePartial<T>, settings: ModelUpdateSettings & {"return": "request"}): Promise<DynamoDB.UpdateItemInput>;
+	update (keyObj: ObjectType, updateObj: UpdatePartial<T>, settings: ModelUpdateSettings & {"return": "request"}, callback: CallbackType<DynamoDB.UpdateItemInput, any>): void;
+	update (keyObj: ObjectType, updateObj: UpdatePartial<T>, settings: ModelUpdateSettings): Promise<T>;
+	update (keyObj: ObjectType, updateObj: UpdatePartial<T>, settings: ModelUpdateSettings, callback: CallbackType<T, any>): void;
+	update (keyObj: ObjectType, updateObj: UpdatePartial<T>, settings: ModelUpdateSettings & {"return": "item"}): Promise<T>;
+	update (keyObj: ObjectType, updateObj: UpdatePartial<T>, settings: ModelUpdateSettings & {"return": "item"}, callback: CallbackType<T, any>): void;
+	update (keyObj: InputKey | ObjectType, updateObj?: UpdatePartial<T> | CallbackType<T, any> | CallbackType<DynamoDB.UpdateItemInput, any>, settings?: ModelUpdateSettings | CallbackType<T, any> | CallbackType<DynamoDB.UpdateItemInput, any>, callback?: CallbackType<T, any> | CallbackType<DynamoDB.UpdateItemInput, any>): void | Promise<T> | Promise<DynamoDB.UpdateItemInput> {
 		if (typeof updateObj === "function") {
 			callback = updateObj as CallbackType<ItemCarrier | DynamoDB.UpdateItemInput, any>; // TODO: fix this, for some reason `updateObj` has a type of Function which is forcing us to type cast it
 			updateObj = null;
diff --git a/signatures/version1/cla.json b/signatures/version1/cla.json
index 2f4c17a0..e4e3a6f1 100644
--- a/signatures/version1/cla.json
+++ b/signatures/version1/cla.json
@@ -127,6 +127,14 @@
       "created_at": "2023-04-30T18:19:09Z",
       "repoId": 16962535,
       "pullRequestNo": 1598
+    },
+    {
+      "name": "lmanerich",
+      "id": 1796542,
+      "comment_id": 1537185548,
+      "created_at": "2023-05-06T17:19:22Z",
+      "repoId": 16962535,
+      "pullRequestNo": 1599
     }
   ]
 }
\ No newline at end of file


Test patch:
diff --git a/packages/dynamoose/test/types/Model.ts b/packages/dynamoose/test/types/Model.ts
index ac1a1e6e..c1753666 100644
--- a/packages/dynamoose/test/types/Model.ts
+++ b/packages/dynamoose/test/types/Model.ts
@@ -98,3 +98,15 @@ export const UserModel = dynamoose.model(
 	"User",
 	userSchema
 );
+
+UserTypedModel.update({"id": "foo"}, {
+	"$SET":{"name":"foo"}
+});
+
+UserTypedModel.update({"id": "foo"}, {
+	"$ADD":{"age":1}
+});
+
+UserTypedModel.update({"id": "foo"}, {
+	"$REMOVE":{"age":null}
+});