How to change the interface key: value: string to [{ [key: string]: string } - string

I have interface
IUser {
name: string;
surname: string;
cityLocation: string;
}
the value will be
const user: IUser = {
"name": "John";
"surname": "Smith";
"cityLocation": "LA";
}
I have adapter, they have method which receive
function adapter(columns: { [key: string]: string }) {...}
when I try to do:
adapter(user);
I have error:
Argument of type 'IUser' is not assignable to parameter of type '{ [key: string]: string; }'.   Index signature is missing in type 'IUser'.
How can I fix it?
(funny, but when I dont use signature IUser for object user - adapter works...)
Hope for your help. Big Thx!

As the error says, Index signature is missing in type 'IUser' so add it:
interface IUser {
name: string;
surname: string;
cityLocation: string;
[key: string]: string;
}
Playground link

Related

Mongoose Map type incompatible with TypeScript type

I have the following interface for model schema:
import { Document } from 'mongoose';
export interface ILog {
tags: { [key: string]: string }[];
}
export interface ILogDocument extends ILog, Document {}
And there is the schema:
const logSchema = new Schema<ILogDocument>({
tags: { type: Map, of: String },
});
So i get the following type error on my schema:
TS2322: Type '{ type: MapConstructor; of: StringConstructor; }' is not assignable to type 'SchemaDefinitionProperty<{ [key: string]: string; }[]> | undefined'.
I want to use proper type defintion, I tried type: Schema.Types.Mixed and its works, but is there a better way to specify mongoose type for tags: { [key: string]: string }[] ?
{ [key: string]: string }[] is not exactly Map. You can use the Map type from TS.
import { Document, Schema } from 'mongoose';
export interface ILog {
tags: Map<string, string>;
}
export interface ILogDocument extends ILog, Document {}
const logSchema = new Schema<ILogDocument>({
tags: { type: Map, of: String }
});

Typescript typing: function arguments go through keys of nested object

interface TestObject {
person: {
firstName: string;
lastName: string;
address: {
country: string;
city: string;
street: string;
}
}
id: string;
}
const t: TestObject = {} as any;
type K<T> = T extends object ? keyof T : never;
function f<T, K1 extends K<T>>(object: T, key: K1): T[K1];
function f<T, K1 extends K<T>, K2 extends K<T[K1]>>(object: T, key1: K1, key2: K2): T[K1][K2];
function f<T, K1 extends K<T>, K2 extends K<T[K1]>, K3 extends K<T[K1][K2]>>(object: T, key1: K1, key2: K2, key3: K3): T[K1][K2][K3];
// ...
function f() {
return 1 as any;
}
const result1 = f(t, 'person', 'address');
/*
const result1: {
country: string;
city: string;
street: string;
}
*/
const result2 = f(t, 'id', '');
/*
Argument of type 'string' is not assignable to parameter of type 'never'.ts(2345)
*/
Can the same be done without "death by a thousand overloads"? If there was something like Tail<T> type alias for const tuples, i think i know how i could do it.

NestJS Prisma - Types of property entities incompatible, missing or not assigned

I'm trying to update my Postgres database using Prisma ORM in NestJS (Microservices architecture).
The code allows users to interact with invitation requests.
But I keep getting the error:
Argument of type 'Invitation' is not assignable to parameter of type 'Invitation & { work: Work; }'. Property 'work' is missing in type 'Invitation' but required in type '{ work: Work; }'.
The Full error message looks like this:
Type '{ status: "PENDING" | "ACCEPTED" | "REJECTED"; work: undefined; id: string; workId: string; coId?: string; createdAt: Date; respondedAt?: Date; owner?: string; note?: string; }' is not assignable to type '(Without<InvitationUpdateInput, InvitationUncheckedUpdateInput> & InvitationUncheckedUpdateInput) | (Without<...> & InvitationUpdateInput)'.
Type '{ status: "PENDING" | "ACCEPTED" | "REJECTED"; work: undefined; id: string; workId: string; coId?: string; createdAt: Date; respondedAt?: Date; owner?: string; note?: string; }' is not assignable to type 'Without<InvitationUncheckedUpdateInput, InvitationUpdateInput> & InvitationUpdateInput'.
Type '{ status: "PENDING" | "ACCEPTED" | "REJECTED"; work: undefined; id: string; workId: string; coId?: string; createdAt: Date; respondedAt?: Date; owner?: string; note?: string; }' is not assignable to type 'Without<InvitationUncheckedUpdateInput, InvitationUpdateInput>'. Types of property 'workId' are incompatible. Type 'string' is not assignable to type 'never'.
How can I fix this?
Thanks in advance!
The Invitation Interface:
export default class Invitation {
id: string;
workId: string;
work: string;
co?: string;
status: RequestStatus;
createdAt: Date;
respondedAt?: Date;
owner?: string;
note?: string;
}
export enum RequestStatus {
PENDING = 'PENDING',
REJECTED = 'REJECTED',
ACCEPTED = 'ACCEPTED',
}
The prisma invitation schema:
model Invitation {
id String #id #db.Uuid
workId String #db.Uuid
work Work #relation(...)
status RequestStatus
co String #db.Uuid
owner String
note String
createdAt DateTime
respondedAt DateTime
}
The Updating mechanics in Postgres Persistence Infrastructure:
async update(invitation: Invitation): Promise<Invitation> {
const entity = await this.prismaService.invitation.update({
where: {
id: invitation.id,
},
data: {
...invitation,
status: RequestStatusEntity[invitation.status],
work: undefined,
},
include: {
work: true,
},
});
return this.toDomain(entity);
}
private toDomain(
entity: InvitationEntity & {
work: WorkEntity;
},
): Invitation {
return Object.setPrototypeOf(
{
...entity,
status: RequestStatus[entity.status],
},
Invitation.prototype,
);
}
You cannot use TypeScript enums in this case as Prisma uses string literals in the generated type.
I would suggest using Prisma's types directly instead of creating your own like this:
import { RequestStatus } from '#prisma/client'
export default class Invitation {
id: string;
workId: string;
work: string;
co?: string;
status: RequestStatus;
createdAt: Date;
respondedAt?: Date;
owner?: string;
note?: string;
}

No overload matches this call error with typescript, nodejs and mongoose

I have a service in an application that was previously working but all of a sudden I am getting this typescript error and I am unsure how to go about solving it. Please see my model file:
import mongoose from "mongoose";
import { AccessType, TicketType } from "./types";
interface TicketAttrs {
name: string;
type: TicketType;
access: AccessType;
uniqueId: string;
price: number;
quantity: number;
creator: string;
}
interface TicketDoc extends mongoose.Document {
name: string;
type: TicketType;
access: AccessType;
uniqueId: string;
price: number;
quantity: number;
creator: string;
}
interface TicketModel extends mongoose.Model<TicketDoc> {
build(attrs: TicketAttrs): TicketDoc;
}
const ticketSchema = new mongoose.Schema(
{
name: {
type: String,
},
type: {
type: String,
enum: Object.values(TicketType),
},
access: {
type: String,
enum: Object.values(AccessType),
},
price: {
type: Number,
},
uniqueId: {
type: String,
},
quantity: {
type: Number,
},
creator: {
type: String,
},
},
{
timestamps: true,
toJSON: {
transform(doc, ret) {
ret.id = ret._id;
delete ret._id;
},
},
}
);
ticketSchema.statics.build = (attrs: TicketAttrs) => {
return new Ticket(attrs);
};
const Ticket = mongoose.model<TicketDoc, TicketModel>("Ticket", ticketSchema);
export { Ticket };
I get an error on ticketSchema on this line const Ticket = mongoose.model<TicketDoc, TicketModel>("Ticket", ticketSchema);
The Error Message is:-
const ticketSchema: mongoose.Schema<mongoose.Document<any>, mongoose.Model<mongoose.Document<any>>>
No overload matches this call.
Overload 1 of 6, '(name: string, schema?: Schema<TicketDoc, TicketModel> | undefined, collection?: string | undefined, skipInit?: boolean | undefined): TicketModel', gave the following error.
Argument of type 'Schema<Document<any>, Model<Document<any>>>' is not assignable to parameter of type 'Schema<TicketDoc, TicketModel>'.
Types of property 'methods' are incompatible.
Type '{ _id?: any; __v?: number | undefined; $ignore: (path: string) => void; $isDefault: (path: string) => boolean; $isDeleted: (val?: boolean | undefined) => boolean; $isEmpty: (path: string) => boolean; ... 47 more ...; validateSync: (pathsToValidate?: string[] | undefined, options?: any) => NativeError | null; } & { ....' is not assignable to type '{ name: string; type: TicketType; access: AccessType; uniqueId: string; price: number; quantity: number; creator: string; _id?: any; __v?: number | undefined; $ignore: (path: string) => void; ... 50 more ...; validateSync: (pathsToValidate?: string[] | undefined, options?: any) => NativeError | null; } & { ...; }'.
Type '{ _id?: any; __v?: number | undefined; $ignore: (path: string) => void; $isDefault: (path: string) => boolean; $isDeleted: (val?: boolean | undefined) => boolean; $isEmpty: (path: string) => boolean; ... 47 more ...; validateSync: (pathsToValidate?: string[] |
I am also getting an error saying that there is no build function, I don't understand what broke the code and it was working without any changes.

How to define ObjectId in DTO & what is the right query to get relational data in NestJS Mongoose?

I'm setting up an app using NestJS, Angular, GraphQL and MongoDB and new to these stacks
I have 2 collections called Tasks & Statuses with sample data
Tasks
[{
"_id": {
"$oid": "5f9138f71163a739c43fc9b3"
},
"TaskDesc": "Meeting with Brian",
"StartedDate": "2020-10-22T07:42:40Z",
"EndDate": "2020-10-22T10:42:40Z",
"StatusId": "5f91375d1163a739c43fc9af"
}]
Statuses
[{
"_id": {
"$oid": "5f91375d1163a739c43fc9af"
},
"StatusDesc": "Done"
}]
Here are schemas defined in NestJS
import { Schema, Types } from 'mongoose';
import { MongooseModule } from '#nestjs/mongoose';
export const TaskSchema = new Schema({
TaskDesc: String,
StartedDate: String,
EndDate: String,
StatusId:
{
type: Types.ObjectId,
ref: 'Statuses'
}
});
export const StatusSchema = new Schema({
StatusDesc: String
});
export const SchemaGroups = MongooseModule.forFeature([
{name: 'Tasks', schema: TaskSchema, collection: 'Tasks'},
{name: 'Statuses', schema: StatusSchema, collection: 'Statuses'}
]);
The DTO
import { ObjectType, Field, ID } from '#nestjs/graphql';
#ObjectType()
export class TasksDTO {
#Field(() => ID)
id?: string;
#Field()
TaskDesc: string;
#Field()
StartedDate: string;
#Field()
EndDate: string;
#Field()
StatusId: string;
}
#ObjectType()
export class StatusDTO {
#Field(() => ID)
readonly id?: string;
#Field()
readonly StatusDesc: string;
}
The model
import { Document, Schema } from 'mongoose';
export interface Tasks extends Document {
readonly TaskDesc : string,
readonly StartedDate: string,
readonly EndDate: string,
readonly StatusId: Schema.Types.ObjectId
}
export interface Status extends Document {
readonly StatusDesc : string
}
The resolver
#Resolver('Tasks')
export class ListTodoResolver {
constructor(private readonly todoItemsService: TodolistService){
}
#Query(() => [TasksDTO])
async Tasks(): Promise<TasksDTO[]> {
return await this.todoItemsService.getAllTasks();
}
#Query(() => [StatusDTO])
async Statuses(): Promise<StatusDTO[]>{
return await this.todoItemsService.getAllStatuses();
}
}
The service
import { Injectable } from '#nestjs/common';
import { InjectModel } from '#nestjs/mongoose';
import { Model } from 'mongoose';
#Injectable()
export class TodolistService {
constructor(
#InjectModel('Tasks') private readonly todoItemsModel: Model<Tasks>,
#InjectModel('Statuses') private readonly statusItemsModel: Model<Status>
) { }
async getAllTasks() : Promise<Tasks[]>{
let tasks = await this.todoItemsModel.find().exec(); // how should we query in the service to get relational data from another collection?
console.log(tasks);
return tasks;
}
async getAllStatuses() : Promise<Status[]>{
return await this.statusItemsModel.find().exec();
}
}
And I had these errors
Type 'Tasks' is not assignable to type 'TasksDTO'. Types of property 'StatusId' are incompatible. Type 'ObjectId' is not assignable to type 'string'.
How do we define the ObjectId type in the DTO ?
My expected output for getAllTasks() method would be
[{
"_id": "5f9138f71163a739c43fc9b3",
"TaskDesc": "Meeting with Brian",
"StartedDate": "2020-10-22T07:42:40Z",
"EndDate": "2020-10-22T10:42:40Z",
"StatusDesc": "Done"
}]
The error is already show where you should fix it.
Type 'Tasks' is not assignable to type 'TasksDTO'. Types of property 'StatusId' are incompatible. Type 'ObjectId' is not assignable to type 'string'.
Which mean your DTO should be
#ObjectType()
export class TasksDTO {
#Field(() => ID)
id?: string;
#Field()
TaskDesc: string;
#Field()
StartedDate: string;
#Field()
EndDate: string;
#Field()
StatusId: objectId; // change here
}
Not sure the object Id type in your DTO, try to look up in the documentation to see if there are Object ID type for the DTO if not you should change StatusId for all place to string and that should work

Resources