dynamodb-toolbox icon indicating copy to clipboard operation
dynamodb-toolbox copied to clipboard

Sub object list type Typescript error

Open vespertilian opened this issue 4 years ago • 3 comments

So I get a type error when trying to have a list of sub-objects on my primary object type.

This is my setup.

export interface MyObject {
  id: string
  created: string
  objects: MySubObjectType[]
}

export interface MySubObjectType {
  foo: string
}

myEntity = new Entity<MyObject>({
  name: 'MyObjectType'
  attributes: {
   id: { partitionKey: true }
   created {sortKey: true}
   objects: {type: 'list'}
  }
})

I get an error Unable to apply to Schema type

Currently schema type looks like this: declare type SchemaType = string | number | boolean | { [key: string]: SchemaType } | SchemaType[];

I think the issue is this part of the SchemaType type ... { [key: string]: SchemaType }, Typescript does not seem to like an object with a {[key: string]: string | ...otherTypes } which is effectively what SchemaType is, see this issue on stack overflow.

The fix based on that stack overflow post is to just have {[key: string]: any}, you still seem to get the correct type hinting with this setup.

Happy to do a PR with this fix, unless you maybe have some more typescript fu than me, and can wrangle something a little better.

Copy past this into typescript playground to play around yourself

export interface MyObject {
  id: string
  created: string
  objects: MySubObjectType[]
}

export interface MySubObjectType {
  foo: string
}

// existing types (do not work)
declare type SchemaType = string | number | boolean | { [key: string]: SchemaType } | SchemaType[];

class Entity<Schema extends {[key in keyof Schema]: SchemaType}> { 
    foo: Schema
    constructor(schema: Schema) {
        this.foo = schema
    }
}

const myEntityInstance: Entity<MyObject> = new Entity({id: '1', created: 'created', objects: [{foo: 'bar'}]})
myEntityInstance.foo.objects[0].foo

// updated types (works)
declare type NewSchemaType = string | number | boolean | { [key: string]: any } | SchemaType[];
class EntityTwo<Schema extends {[key in keyof Schema]: NewSchemaType}> { 
    foo: Schema
    constructor(schema: Schema) {
        this.foo = schema
    }
}

const myEntityInstanceTwo: EntityTwo<MyObject> = new EntityTwo({id: '1', created: 'created', objects: [{foo: 'bar'}]})
myEntityInstanceTwo.foo.objects[0].foo

vespertilian avatar Feb 03 '21 05:02 vespertilian

@jeremydaly any update on this? I would like to have the definition like this

  export declare type SchemaType = string | number | boolean | null | undefined | Record<string, SchemaType> | SchemaType[];

dheeraj1447 avatar Jun 03 '21 13:06 dheeraj1447

At least include undefined in the SchemaType? This would allow to write something like:

export interface MyObject {
  id: string
  name?: string
}

instead of:

export interface MyObject {
  id: string
  name: string | null
}

Wingjam avatar Jan 25 '22 21:01 Wingjam

Is there a suitable workaround for this? I am currently having to type my object as any:

interface IQuestionnaire {
  questions: IQuestionnaireQuestion[] //having to change this to any
}

shesupplypi avatar May 01 '22 23:05 shesupplypi

Hey @vespertilian, with the latest version you can achieve what you're looking for.

export interface MyObject {
	id: string;
	created: string;
	objects: MySubObjectType[];
}

export interface MySubObjectType {
	foo: string;
}

const myEntity = new Entity<'MyEntityName', MyObject>({
	name: 'MyEntityName',
	attributes: {
	 id: { partitionKey: true },
	 created: {sortKey: true},
	 objects: {type: 'list'},
	}
})

naorpeled avatar Nov 04 '22 22:11 naorpeled

Is there a suitable workaround for this? I am currently having to type my object as any:

interface IQuestionnaire {
  questions: IQuestionnaireQuestion[] //having to change this to any
}

Let me know if the solution I've mentioned above doesn't work for you, it should work for you as well 🙏

naorpeled avatar Nov 04 '22 23:11 naorpeled

Hi, thanks so much for the reply! I will upgrade to the latest version and try this out.

shesupplypi avatar Nov 05 '22 02:11 shesupplypi

Hi, thanks so much for the reply! I will upgrade to the latest version and try this out.

Awesome, sounds good, let me know if you need anything!

naorpeled avatar Nov 05 '22 02:11 naorpeled

I'll be closing this issue for now, @shesupplypi feel free to ping me if you need anything 😎

naorpeled avatar Nov 06 '22 16:11 naorpeled

@naorpeled Quick one related to this, when I use types like this I'm getting an error when I add the table property as per the docs:


const DocumentClient = new DynamoDB.DocumentClient({
  convertEmptyValues: false,
  ...options,
});

const MyTable = new Table({
  name: config.TABLE_NAME,

  partitionKey: "sub",

  DocumentClient
});

export interface NotificationType {
  id: string;
  notificationTimes: {
    time: string;
    hasBeenSent: boolean;
  }[];
}

const NotificationORM = new Entity<'notification', NotificationType>({

  name: "notification",
  attributes: {
    id: { partitionKey: true }, 
    notificationTimes: {type: 'list'},
  },
  // Assign it to our table
  table: MyTable
} as const)

Types of property 'table' are incompatible. Type 'Table<string, "sub", Key>' is not assignable to type 'undefined'

Do I need to add some types in the table creation as well?

jvgeee avatar Mar 14 '23 23:03 jvgeee

@naorpeled Quick one related to this, when I use types like this I'm getting an error when I add the table property as per the docs:


const DocumentClient = new DynamoDB.DocumentClient({
  convertEmptyValues: false,
  ...options,
});

const MyTable = new Table({
  name: config.TABLE_NAME,

  partitionKey: "sub",

  DocumentClient
});

export interface NotificationType {
  id: string;
  notificationTimes: {
    time: string;
    hasBeenSent: boolean;
  }[];
}

const NotificationORM = new Entity<'notification', NotificationType>({

  name: "notification",
  attributes: {
    id: { partitionKey: true }, 
    notificationTimes: {type: 'list'},
  },
  // Assign it to our table
  table: MyTable
} as const)

Types of property 'table' are incompatible. Type 'Table<string, "sub", Key>' is not assignable to type 'undefined'

Do I need to add some types in the table creation as well?

You need to remove the generics from the entity. If you want to use the type of the entity you can do the following

type Notification = EntityItem<typeof NotificationORM>

let me know if you have any further questions 🙏

naorpeled avatar Mar 15 '23 16:03 naorpeled