I have this DTO and when I try to convert it to an object it doesn't convert to the way I want. Value converted to object but field name remains the same.
export class CreatePageGroupsDto {
#IsString()
#Expose()
name: string;
#IsString()
#Expose()
url: string;
#IsEnum(CategoryEnum)
#Expose()
category: CategoryEnum;
#Expose({ name: 'page_view' })
#Transform(({
value = false,
}, ) => {
const pageView: PageView = { stand_alone: value };
return pageView;
} )
stand_alone?: boolean;
}
I have this DTO and want to convert it to an object like this
{
'name': 'string',
'url': 'string',
'category': 'legal',
'page_view': {
stand_alone: false,
},
}
If you have the dto instance, and you want to covert it to an object. Your code is right.
import { Expose, instanceToPlain, Transform } from 'class-transformer';
class CreatePageGroupsDto {
#Expose({ name: 'page_view' })
#Transform(({ value = false }) => {
const pageView = { stand_alone: value };
return pageView;
})
stand_alone?: boolean;
}
const dto = new CreatePageGroupsDto();
dto.stand_alone = false;
console.log(instanceToPlain(dto));
Output:
{ page_view: { stand_alone: false } }
So I think you actually have a plain object from http request. And your framework like Nestjs convert the request object to the dto instance. This process is plainToInstance, not instanceToPlain. You can try swapping page_view and stand_alone like the following code:
class CreatePageGroupsDto {
#Expose({ name: 'stand_alone' })
#Transform(({ value = false }) => {
const pageView = { stand_alone: value };
return pageView;
})
page_view?: boolean;
}
Related
I have a typeorm entity that uses single table inheritance:
#Entity()
#TableInheritance({ column: { type: "varchar", name: "type" } })
#ObjectType({ isAbstract: false })
export class OrganisationEntity extends BaseEntity {
#Field()
#Column()
name: string;
#Field(() => [UserEntity])
#OneToMany(() => UserEntity, (user) => user.organisation)
users: UserEntity[];
}
and some child entities:
#ChildEntity()
#ObjectType()
class MedicalOrganisation {
}
#ChildEntity()
#ObjectType()
class SoftwareOrganisation {
}
#ChildEntity()
#ObjectType()
class MedicalOrganisation {
}
I'm wondering how I can get the type and the child properties from the parent organisation so that I can do something like:
const organisation = await OrganisationEntity.findOne()
if(organisation.type === "medicalOrganisation"){
...
}
But it seems I'm not allowed to access the type property through the parent. Does anyone know how this can be done?
I'd prefer not to use instanceof because it requires the child entities and is causing circular dependencies.
You've got two options. Either use .createQueryBuilder and get the item using getRawOne and the returned object would contain a field named OrganisationEntity_type which can be used to do the checks. It's value would either be 'MedicalOrganisation', or 'SoftwareOrganisation' and so on.
const orgRepository: Repository<OrganisationEntity> = getRepository(OrganisationEntity);
let organisation = await orgRepository.createQueryBuilder().getRawOne();
// organisation = {
// OrganisationEntity_name: 'name',
// OrganisationEntity_users: [
// {},
// {}
// ],
// OrganisationEntity_type: 'MedicalOrganisation'
// }
Or, you could add the field type in the OrganisationEntity itself like this:
#Entity()
#TableInheritance({ column: { type: "varchar", name: "type" } })
#ObjectType({ isAbstract: false })
export class OrganisationEntity extends BaseEntity {
#Field()
#Column()
name: string;
#Field(() => [UserEntity])
#OneToMany(() => UserEntity, (user) => user.organisation)
users: UserEntity[];
#Column
type: string;
}
and do a straightforward:
let organisation = await OrganisationEntity.findOne();
// organisation =
// MedicalOrganisation {
// name: 'name',
// users: [{}, {}],
// type: 'MedicalOrganisation'
// }
and you get the type field in the object itself.
I am trying to type de values of an array in a template object.
Currently I have achieved my goal using objects like so :
// defining the model type
interface RouteModel {
route: string
params?: Record<string, string>
}
interface RoutesModel {
[routeName: string]: RouteModel
}
// value constructor
function makeRoutes<T extends RoutesModel>(input: T) {
return input
}
// type safe creation for routes
const routes = makeRoutes({
potato: {
route: '/potato/:potatoId/rate',
params: { potatoId: '' },
},
grapes: {
route: 'grapes',
},
banana: {
route: 'bag/:bagId/:bananaId',
params: { bagId: '', bananaId: '' },
},
})
const useTypedHistory = <T extends RoutesModel>() => {
const navigate = <K extends keyof T>(route: K, params: Record<keyof T[K]['params'], string>) => {
}
return { navigate }
}
const Component = () => {
const { navigate } = useTypedHistory<typeof routes>()
navigate('banana', { bagId: '123', bananaId: '567' })
ʌ --- type safety works here, it requires the right object depending on the first param
return null
}
export default useTypedHistory
My problem is that at the beginning, I declare my params as an object with the correct keys but empty string to make it work.
I would like to an array of strings instead, so it would look like this:
// defining the model type
interface RouteModel {
route: string
params?: string[]
}
interface RoutesModel {
[routeName: string]: RouteModel
}
// value constructor
function makeRoutes<T extends RoutesModel>(input: T) {
return input
}
// type safe creation for routes
const routes = makeRoutes({
potato: {
route: '/potato/:potatoId/rate',
params: ['potatoId'],
},
grapes: {
route: 'grapes',
},
banana: {
route: 'bag/:bagId/:bananaId',
params: ['bagId', 'bananaId'],
},
})
const useTypedHistory = <T extends RoutesModel>() => {
const navigate = <K extends keyof T>(route: K, params: Record<(valueof T[K]['params']), string>) => {
ʌ --- does not work
}
return { navigate }
}
But that doesn't work at all. From what I've seen, valueof would allow me to infer the values, but I can't seem to make it work.
Any help would be very much appreciated
With the Vue composition API we created the following composable:
import { computed, reactive, SetupContext, ref } from '#vue/composition-api'
export const useApplications = (root: SetupContext['root']) => {
const applications = reactive({
1: {
name: ref(root.$t('app1.name')),
description: ref(root.$t('app1.description')),
formName: 'app1form',
},
2: {
name: ref(root.$t('app2.name')),
description: ref(root.$t('app2.description')),
formName: 'app2form',
},
})
const getApplication = (id: string) => {
return applications[id]
}
return {
applications: computed(() => applications),
getApplication,
}
}
Although the code works fine it generates the TS error:
#typescript-eslint/no-unsafe-return: Unsafe return of an any typed value
When hovering over the applications section it's clear that typescript recognizes the types except for the property name (1):
Do we need to create an interface to solve this and do we have to redefine each and every property in the interface? I tried something like this but it is incorrect:
interface IApplication {
[key]: string {
name : string
description: string
formName: string
}
}
TypeScript doesn't generally type things so they can be indexed by any string key. As you say, you can define an interface for it:
interface IApplications {
[key: string]: {
name : string;
description: string;
formName: string;
};
}
// ...
const applications: IApplications = ...
Or you might just use a type for the object part of that and use the built-in Record type for applications:
interface IApplication {
name : string;
description: string;
formName: string;
}
// ...
const applications: Record<string, IApplication> = ...
Or combining the two:
interface IApplication {
name : string;
description: string;
formName: string;
}
type IApplications = Record<string, IApplication>;
(Or you can inline the IApplication part. Or... :-) )
According to the structure of the parameters of reactive function, the interface could be defined like :
interface IApplication {
[key:string]:{
name : string
description: string
formName: string
}
}
and
const applications = reactive<IApplication>({ ...
But I want to suggest another approach which define tha applications as reactive parameter which has an array as value:
import { computed, reactive, SetupContext, ref ,toRef} from '#vue/composition-api'
interface IApplication {
name : string
description: string
formName: string
}
export const useApplications = (root: SetupContext['root']) => {
const state= reactive<Array<IApplication>>({ applications :
[{
name: ref(root.$t('app1.name')),
description: ref(root.$t('app1.description')),
formName: 'app1form',
},
{
name: ref(root.$t('app2.name')),
description: ref(root.$t('app2.description')),
formName: 'app2form',
}],
})
const getApplication = (index: number) => {
return state.applications[index]
}
return {
applications: toRef(state,'applications'),
getApplication,
}
}
Suppose I have the following interfaces:
interface Person {
name: string;
}
interface Attendee {
person: Person;
id: number;
}
I have already figured out how to use the compiler API to extract string representations of every property's type, e.g.:
{
Attendee: {
person: "Person",
id: "number"
}
}
Here's how I do it: https://github.com/jlkiri/tsx-ray/blob/master/src/index.ts.
It's a combination of typeToString and getTypeOfSymbolAtLocation of the Type Checker.
However I would like to resolve types likes Person to their definition so that I get:
{
Attendee: {
person: {
name: "string";
},
id: "number"
}
}
Is there API I can use to easily do this, or do I have to implement the logic myself?
Check ts-morph. I recently discovered it and it looks promising.
Here is a minimal code that can do what you want:
import {ClassDeclaration, Project} from 'ts-morph';
const project = new Project({);
project.addSourceFilesAtPaths("src/**/*.ts");
const allSourceFiles = project.getSourceFiles();
allSourceFiles.forEach(sourceFile => {
const classes = sourceFile.getClasses();
classes.forEach(cls => {
console.log(`class ${cls.getName()} {`);
const properties = cls.getProperties();
properties.forEach(prop => {
const type = prop.getType();
if(type.isClassOrInterface()) {
const typeSymbol = type.getSymbol();
console.log(` ${prop.getName()} :
${typeSymbol?.getName()} {`);
const clsDeclaration = typeSymbol?.getDeclarations()[0] as ClassDeclaration;
const members = clsDeclaration.getMembers();
members.forEach(m => {
console.log(` ${m.getText()}`);
});
console.log(` }`);
} else {
console.log(` ${prop.getName()} : ${type.getText()}`);
}
});
console.log(`}`);
});
})
For the following two files:
// ./src/property.ts
class Category {
description: string;
id: number;
}
export default Category;
// ./src/product.ts
import Category from './category';
class Product {
name: string;
price: number;
category: Category;
}
export default Product;
you will get the following printout:
class Category {
description : string
id : number
}
class Product {
name : string
price : number
category : Category {
description: string;
id: number;
}
}
I would like to set a resolver, on an individual field that returns a string.
For this example. I want to take the title attribute, and make it .toUpperCase
Schema
type Product {
title(uppercase:Boolean!): String!
}
type Query {
products: [Product]
}
Resolver
Query: {
products: () => [{title:'foo'}],
products.title: (stringToRtn, { action }) => {
return action ? stringToRtn.toUpperCase : stringToRtn
}
}
Here is the solution:
const resolvers = {
Product: {
title: product => {
return product.title.toUpperCase();
}
},
Query: {
products: () => [{title:'foo'}]
}
};
If you're using TypeScript, use these typeDefs:
type Product {
title: String!
}
type Query {
products: [Product]
}
Another way is to use custom directive like "#upperCase", but it's too complex for this.
TypeScript update directive way
Remove : GraphQLField<any, any> if you're not using TypeScript.
#uppercase directive implementation:
import { SchemaDirectiveVisitor } from 'graphql-tools';
import { GraphQLField, defaultFieldResolver } from 'graphql';
class UppercaseDirective extends SchemaDirectiveVisitor {
public visitFieldDefinition(field: GraphQLField<any, any>) {
const { resolve = defaultFieldResolver } = field;
field.resolve = async function resolver(...args) {
const result = resolve.apply(this, args);
if (typeof result === 'string') {
return result.toUpperCase();
}
return result;
};
}
}
export { UppercaseDirective };
If you're using TypeScript use these typeDefs:
const typeDefs: string = `
enum Status {
SOLD_OUT
NO_STOCK
OUT_OF_DATE #deprecated(reason: "This value is deprecated")
}
type Book {
id: ID!
title: String #uppercase
author: String
status: Status
name: String #deprecated(reason: "Use title instead")
}
type Query {
books: [Book]!
bookByStatus(status: Status!): [Book]!
}
`;
schema:
(Remove : GraphQLSchema if you're not using TypeScript.)
const schema: GraphQLSchema = makeExecutableSchema({
typeDefs,
resolvers,
schemaDirectives: {
deprecated: DeprecatedDirective,
uppercase: UppercaseDirective
}
});
Here is a link to source code using TypeScript