Skip to content

Commit

Permalink
test: fail on unknown typed method access
Browse files Browse the repository at this point in the history
  • Loading branch information
tranhl committed Mar 24, 2024
1 parent f825906 commit f13f9d5
Show file tree
Hide file tree
Showing 12 changed files with 46 additions and 27 deletions.
4 changes: 2 additions & 2 deletions packages/dynamoose/lib/General.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export type KeyObject = {[attribute: string]: string | number};
// An item representing a DynamoDB key
export type InputKey = string | number | KeyObject;

interface ModelItemConstructor<T extends Item, U extends ItemMethods> {
interface ModelItemConstructor<T extends Item, U extends ItemMethods<U>> {
/**
* In order to create a new item you just pass in your object into an instance of your model.
*
Expand All @@ -33,7 +33,7 @@ interface ModelItemConstructor<T extends Item, U extends ItemMethods> {
new (object: Flatten<Omit<T, keyof Item>>): T & U;
Model: Model<T, U>;
}
export type ModelType<T extends Item, U extends ItemMethods> = T & Model<T, U> & ModelItemConstructor<T, U>;
export type ModelType<T extends Item, U extends ItemMethods<U>> = T & Model<T, U> & ModelItemConstructor<T, U>;

// This represents an item array. This is used for the output of functions such as `scan`, `query`, and `batchGet`. These functions can extend this property to add additional properties or functions. However this represents the shared properties/functions for all item arrays.
export interface ItemArray<T> extends Array<T> {
Expand Down
6 changes: 3 additions & 3 deletions packages/dynamoose/lib/Item.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,9 @@ export interface ItemSettings {
type?: "fromDynamo" | "toDynamo";
}

export interface ItemMethods {
[key: string]: Function;
}
export type ItemMethods<T> = {
[K in keyof T]: Function
};

interface ItemInternalProperties {
originalObject: any;
Expand Down
2 changes: 1 addition & 1 deletion packages/dynamoose/lib/Model/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ interface ModelInternalProperties {
}

// Model represents a single entity (ex. User, Movie, Video, Order)
export class Model<T extends ItemCarrier = AnyItem, U extends ItemMethods = ItemMethods> extends InternalPropertiesClass<ModelInternalProperties> {
export class Model<T extends ItemCarrier = AnyItem, U extends ItemMethods<U> = {}> extends InternalPropertiesClass<ModelInternalProperties> {
/**
* This method is the basic entry point for creating a model in Dynamoose. When you call this method a new model is created, and it returns an item initializer that you can use to create instances of the given model.
*
Expand Down
2 changes: 1 addition & 1 deletion packages/dynamoose/lib/ModelStore.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const {internalProperties} = Internal.General;

let models: {[name: string]: Model<Item>} = {};

const returnObject = <T extends Item, U extends ItemMethods>(input: Model<T, U> | string): Model<T, U> | never => {
const returnObject = <T extends Item, U extends ItemMethods<U>>(input: Model<T, U> | string): Model<T, U> | never => {
if (input instanceof Model) {
models[input.name] = input;
return input;
Expand Down
4 changes: 2 additions & 2 deletions packages/dynamoose/lib/Schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ const attributeTypes: (DynamoDBTypeResult | DynamoDBSetTypeResult)[] = utils.arr

type GeneralValueType = string | boolean | number | Buffer | Date;
export type ValueType = GeneralValueType | {[key: string]: ValueType} | ValueType[];
type AttributeType = string | StringConstructor | BooleanConstructor | NumberConstructor | typeof Buffer | DateConstructor | ObjectConstructor | ArrayConstructor | SetConstructor | symbol | Schema | ModelType<Item, ItemMethods>;
type AttributeType = string | StringConstructor | BooleanConstructor | NumberConstructor | typeof Buffer | DateConstructor | ObjectConstructor | ArrayConstructor | SetConstructor | symbol | Schema | ModelType<Item, {}>;

export interface TimestampObject {
createdAt?: string | string[] | SchemaDefinition;
Expand Down Expand Up @@ -324,7 +324,7 @@ interface IndexDefinition {
}
interface AttributeDefinitionTypeSettings {
storage?: "milliseconds" | "seconds" | "iso";
model?: ModelType<Item, ItemMethods>;
model?: ModelType<Item, ItemMethods<{}>>;
attributes?: string[];
separator?: string;
value?: string | boolean | number;
Expand Down
4 changes: 2 additions & 2 deletions packages/dynamoose/lib/Serializer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ interface SerializerInternalProperties {
serializers: {[key: string]: SerializerOptions};
defaultSerializer?: string;

serializeMany: (itemsArray: ModelType<Item, ItemMethods>[], nameOrOptions: SerializerOptions | string) => ObjectType[];
serializeMany: (itemsArray: ModelType<Item, {}>[], nameOrOptions: SerializerOptions | string) => ObjectType[];
serialize: (item: ObjectType, nameOrOptions: SerializerOptions | string) => ObjectType;
}

Expand All @@ -32,7 +32,7 @@ export class Serializer extends InternalPropertiesClass<SerializerInternalProper
"modify": (serialized: ObjectType, original: ObjectType): ObjectType => ({...original})
}
},
"serializeMany": (itemsArray: ModelType<Item, ItemMethods>[], nameOrOptions: SerializerOptions | string): ObjectType[] => {
"serializeMany": (itemsArray: ModelType<Item, ItemMethods<{}>>[], nameOrOptions: SerializerOptions | string): ObjectType[] => {
if (!itemsArray || !Array.isArray(itemsArray)) {
throw new CustomError.InvalidParameter("itemsArray must be an array of item objects");
}
Expand Down
2 changes: 1 addition & 1 deletion packages/dynamoose/lib/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {Instance} from "./Instance";
import {custom as TableDefaults} from "./Table/defaults";
import returnModel from "./utils/dynamoose/returnModel";

const model = <T extends Item = AnyItem, U extends ItemMethods = ItemMethods>(name: string, schema?: Schema | SchemaDefinition | (Schema | SchemaDefinition)[], options?: ModelTableOptions): ModelType<T, U> => {
const model = <T extends Item = AnyItem, U extends ItemMethods<U> = {}>(name: string, schema?: Schema | SchemaDefinition | (Schema | SchemaDefinition)[], options?: ModelTableOptions): ModelType<T, U> => {
let model: Model<T, U>;
let storedSchema: Model<T, U>;
if (name) {
Expand Down
2 changes: 1 addition & 1 deletion packages/dynamoose/lib/utils/dynamoose/returnModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {ModelType} from "../../General";
import {AnyItem, Item, ItemMethods} from "../../Item";
import {Model} from "../../Model";

export default <T extends Item = AnyItem, U extends ItemMethods = ItemMethods>(model: Model<T>): ModelType<T, U> => {
export default <T extends Item = AnyItem, U extends ItemMethods<U> = {}>(model: Model<T, U>): ModelType<T, U> => {
const returnObject: any = model.Item;
const keys = utils.array_flatten([
Object.keys(model),
Expand Down
18 changes: 8 additions & 10 deletions packages/dynamoose/test/types/Item.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import {UserTypedModel} from "./Model";

const user = new UserTypedModel({"id": "1", "name": "Jane", "age": 30});
const typedUser = new UserTypedModel({"id": "1", "name": "Jane", "age": 30});

const shouldPassSave = user.save();
const shouldPassSaveWithReturnRequest = user.save({"return": "request"});
const shouldPassSaveWithReturnItem = user.save({"return": "item"});
const shouldPassSaveCallback = user.save(() => {});
const shouldPassSaveWithReturnRequestCallback = user.save({"return": "request"}, () => {});
const shouldPassSaveWithReturnItemCallback = user.save({"return": "item"}, () => {});
const shouldPassSave = typedUser.save();
const shouldPassSaveWithReturnRequest = typedUser.save({"return": "request"});
const shouldPassSaveWithReturnItem = typedUser.save({"return": "item"});
const shouldPassSaveCallback = typedUser.save(() => {});
const shouldPassSaveWithReturnRequestCallback = typedUser.save({"return": "request"}, () => {});
const shouldPassSaveWithReturnItemCallback = typedUser.save({"return": "item"}, () => {});

// @ts-expect-error
const shouldFailWithInvalidReturnType = user.save({"return": "invalid-return-type"});

const shouldPassCustomMethodAccess = user.resetPassword();
const shouldFailWithInvalidReturnType = typedUser.save({"return": "invalid-return-type"});
12 changes: 12 additions & 0 deletions packages/dynamoose/test/types/ItemMethods.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
/* eslint @typescript-eslint/no-unused-vars: 0 */

import {UserModel, UserTypedModel} from "./Model";

const user = new UserModel({"id": "1", "name": "Jane", "age": 30});
const typedUser = new UserTypedModel({"id": "1", "name": "Jane", "age": 30});

const shouldPassUntypedCustomMethodAccess = user.resetPassword();

const shouldPassCustomMethodAccess = typedUser.resetPassword();
// @ts-expect-error
const shouldFailUnknownCustomMethodAccess = typedUser.updateEmail();
11 changes: 7 additions & 4 deletions packages/dynamoose/test/types/Model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,14 @@ const shouldFailWithInvalidConditionTransaction = model.transaction.condition(0,

// Typed Models

export interface UserMethods extends ItemMethods {
export interface UserMethods {
resetPassword: () => Promise<void>;
}

interface InvalidMethods {
updateEmail: string;
}

export interface User extends Item {
id: string;
name: string;
Expand All @@ -99,6 +103,8 @@ export const UserTypedModel = dynamoose.model<User, UserMethods>(
"User",
userSchema
);
// @ts-expect-error
const shouldFailWithInvalidMethodTypes = dynamoose.model<User, InvalidMethods>("User", userSchema);

export const UserModel = dynamoose.model(
"User",
Expand All @@ -116,6 +122,3 @@ UserTypedModel.update({"id": "foo"}, {
UserTypedModel.update({"id": "foo"}, {
"$REMOVE":{"age":null}
});

// @ts-expect-error
const shouldFailItemCustomMethodAccess = UserTypedModel.resetPassword();
6 changes: 6 additions & 0 deletions packages/dynamoose/test/types/ModelMethods.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/* eslint @typescript-eslint/no-unused-vars: 0 */

import {UserTypedModel} from "./Model";

// @ts-expect-error
const shouldFailItemCustomMethodAccess = UserTypedModel.resetPassword();

0 comments on commit f13f9d5

Please sign in to comment.