how to attach a toArray function to a interface property? - node.js

Consider this setup.
interface C {
foo: string
}
interface A {
B: Record<string, C>
// ... some more properties
}
const test: A = {
B: { hi: {foo: "hello"} }
// ... some more properties
}
now I want to be able to do
test.B.toArray()
that would do
Object.values(test.B)
Now here's a partial solution I came up with that I'm not satisfied with.
interface C {
foo: string;
}
interface A {
B: { value: Record<string, C>; toArray: () => Array<C> };
}
const test: A = {
B: {
value: { hi: { foo: "hello" } },
toArray: function (): Array<C> {
return Object.values(this.value);
},
},
};
//Why I am not happy with this solution is I now have to refer to
test.B
// as
test.B.value

By using the & you will be able to say that you want the interface also implement a function.
interface A {
B: Record<string, C & { toArray: () => Array<C> }>
// ... some more properties
}

Related

Typescript: generic object with dynamic number of keys and values

Currently, my code looks like this:
interface TestInterface<T extends string> {
data: {
[key in T]: () => void;
}
}
const testCommand: TestInterface<"fun1"> = {
data: {
fun1: () => {
// do something
},
}
};
testCommand.data.fun1();
I pass a string (in this example "fun1") as a generic to the testCommand function. In the data object, I can access this string/function "fun1".
My Question: Can I pass an array of strings as a generic to have a custom amount of functions in the data object (Maybe convert the data obj to an array)?
Example:
const testCommand: TestInterface<["fun1", "fun2", "fun3"]> = {
data: {
fun1: () => {
// do something
},
fun2: () => {
// do something
},
fun3: () => {
// do something
},
}
};
or:
const testCommand: TestInterface<["fun1", "fun2", "fun3"]> = {
data: [
{ name: "fun1", exec: () => {} },
{ name: "fun2", exec: () => {} },
{ name: "fun3", exec: () => {} },
]
};
Instead of an array, you can just pass in a union of the string literal types you want to support; the resulting mapped type will contain all of the keys:
const testCommand: TestInterface<"fun1" | "fun2" | "fun3"> = {
data: {
fun1: () => {
// do something
},
fun2: () => {
// do something
},
fun3: () => {
// do something
},
}
};
Indeed, this is very much like using the Record<K, V> utility type, where you pass in a union of key types for K and a value type for V, and get a type with properties of type V at all keys in K.
Playground link to code

Apollo GraphQL resolvers how to pass arguments to query child type

I have a Query that takes an argument with child type which also takes an argument. I would like to pass arguments on both the query and the query child type. I need help on how to implement this logic.
When I hard code the "after" variable the app works fine. How do I implement the resolver to get the after variable from the front-end and then pass is to playerInFoAPI in the dataSources?
SCHEMA
const { gql } = require("apollo-server-express");
const typeDefs = gql`
scalar Date
type Query {
text: String!
club(slug: String!): Club!
}
type Club {
id: ID!
name: String!
pictureSecondaryUrl: String
domesticLeague: DomesticLeague
players(first: Int, after: String): PlayerConnection!
}
type PlayerConnection {
edges: [playerEdge!]!
nodes: [Player!]!
pageInfo: PageInfo!
}
type PageInfo {
endCursor: String
hasNextPage: Boolean!
hasPreviousPage: Boolean!
startCursor: String
}
type Player {
id: ID!
displayName: String!
slug: String!
age: Int!
birthDate: Date
position: String!
country: Country!
subscriptionsCount: Int!
pictureUrl: String
shirtNumber: Int
status: PlayerStatus!
activeClub: Club
allSo5Scores: So5ScoreConnection!
}
type playerEdge {
cursor: String!
node: Player
}
type Country {
code: String!
}
type PlayerStatus {
id: ID!
lastFifteenSo5Appearances: Int
lastFifteenSo5AverageScore: Float
lastFiveSo5Appearances: Int
lastFiveSo5AverageScore: Float
playingStatus: String
}
type So5ScoreConnection {
nodes: [So5Score!]!
}
type So5Score {
score: Float
}
type DomesticLeague {
id: ID!
displayName: String!
}
`;
module.exports = typeDefs;
GRAPHQL DATA SOURCE WITH QUERY
const { GraphQLDataSource } = require("apollo-datasource-graphql");
const { gql } = require("apollo-server-express");
const PLAYER_INFO = gql`
query PLAYER_INFO($slug: String!, $after: String) {
club(slug: $slug) {
players(first: 2, after: $after) {
pageInfo {
endCursor
hasNextPage
hasPreviousPage
startCursor
}
edges {
# start node
node {
id
displayName
slug
age
birthDate
position
country {
slug
code
}
subscriptionsCount
pictureUrl
shirtNumber
activeClub {
id
name
pictureSecondaryUrl
domesticLeague {
id
displayName
}
}
status {
id
lastFifteenSo5Appearances
lastFifteenSo5AverageScore
lastFiveSo5Appearances
lastFiveSo5AverageScore
playingStatus
}
allSo5Scores {
nodes {
score
}
}
} #end node
}
}
}
}
`;
class PlayerInfoAPI extends GraphQLDataSource {
constructor() {
super();
this.baseURL = "https://api.sorare.com/graphql/";
}
async getPlayerInfo(slug,after) {
try {
const response = await this.query(PLAYER_INFO, {
variables: {
slug,
after
},
});
return this.playerInfoReducer(response.data.club.players);
} catch (err) {
console.log(err);
throw new Error(err.message);
}
}
playerInfoReducer(data) {
return {
players: {
pageInfo: {
endCursor: data.pageInfo.endCursor,
startCursor: data.pageInfo.startCursor,
hasNextPage: data.pageInfo.hasNextPage,
hasPreviousPage: data.pageInfo.hasPreviousPage,
},
},
};
}
}
module.exports = PlayerInfoAPI;
RESOLVER
const dateScalar = require("../Utils/CustomDate");
const resolvers = {
Date: dateScalar,
Query: {
text: () => "Hello There!",
club: (_, { slug }, { dataSources }) =>
dataSources.playerInfoAPI.getPlayerInfo(slug),
},
// Club: {
// players(_, { after }, { dataSources }) {
// return dataSources.playerInfoAPI.getPlayerInfo(after);
// },
// },
};
module.exports = resolvers;
FRONT END WITH FETCHMORE FUNCTION
const SLUG = "slug-example";
const PlayerListTable = () => {
const { data, loading, error, networkStatus, fetchMore } = useQuery(
PLAYERS_INFO,
{
variables: { slug: SLUG, after: null },
notifyOnNetworkStatusChange: true,
}
);
const onLoadMore = () => {
//destructure end cursor
const { endCursor } = data.club.players.pageInfo;
console.log(endCursor);
fetchMore({
variables: {
after: endCursor,
},
updateQuery: (prevResult, { fetchMoreResult }) => {
console.log(fetchMoreResult);
},
});
};
You cannot simply forward args to child resolver as this would collide with child args if any. Also the API does not reveal args passed to parent from within a child resolver. Remember that first argument of child resolver will always be whatever is returned from parent resolver. This lets you can pass data from parent to child. This is by design to achieve separation of concerns.

call function from same class nodejs

I try to call function from same class but it always return an error TypeError: this.c is not a function I tried also module.exports.c() and the same result
module.exports = (options)=>{
return{
a:(a)=>{
console.log(a);
},
b:(b)=>{
this.c('c');
console.log(b)
},
c:(c)=>{
console.log(c);
}
}
}
After Updated
module.exports = ({})=>{
return{
genereate:function(identifier){
console.log('genereate')
},
middleware:function(req,res,next){
this.c();
console.log('genereate')
},
c:function(){
console.log('geeet');
}
}
}
Arrow functions bind this lexically (meaning it does not bind it's own this).
Use normal function expressions instead:
module.exports = (options) => {
return {
a: function(a){
console.log(a);
},
b: function(b){
this.c('c');
console.log(b)
},
c: function(c){
console.log(c);
}
};
};
Browserfied example:
let f = (options) => {
return {
a: function(a){
console.log(a);
},
b: function(b){
this.c('c');
console.log(b)
},
c: function(c){
console.log(c);
}
};
};
f().a("a");
f().b("b");
f().c("c");
You can try and export a class, just pass options to your constructor
class InnerCall {
constructor(options) {
this.options = options;
}
a(a) {
console.log(a);
}
b(b) {
this.c('c');
console.log(b);
}
c(c) {
console.log(c);
}
}
const example = new InnerCall({ option: 'Im an option' });
example.b('check this out');
console.log(example.options.option);

How to receive an array as member of an input parameter of a GraphQL service?

Given this schema:
input TodoInput {
id: String
title: String
}
input SaveInput {
nodes: [TodoInput]
}
type SavePayload {
message: String!
}
type Mutation {
save(input: SaveInput): SavePayload
}
Given this resolver:
type TodoInput = {
id: string | null,
title: string
}
type SaveInput = {
nodes: TodoInput[];
}
type SavePayload = {
message: string;
}
export const resolver = {
save: (input: SaveInput): SavePayload => {
input.nodes.forEach(todo => api.saveTodo(todo as Todo));
return { message : 'success' };
}
}
When I sent this request:
mutation {
save(input: {
nodes: [
{id: "1", title: "Todo 1"}
]
}) {
message
}
}
Then the value for input.nodes is undefined on the server side.
Does anybody knows what am I doing wrong?
Useful info:
The mutation works properly with scalar values (such as String as input and return)
I'm using typescript, express and express-graphql.
You need to make changes in the key in the resolver,
export const resolver = {
save: (args: {input: SaveInput}): SavePayload => {
args.input.nodes.forEach(todo => api.saveTodo(todo as Todo));
return { message : 'success' };
}
}

how to serialize a class in an object to string

I have following object ICoreFileStat, which on the server side, URI will be a class created using vscode-uri. How do I convert only URI to string recursively?
export interface ICoreFileStat extends ICoreBaseStat {
children?: ICoreFileStat[];
}
export interface ICoreBaseStat {
resource: URI | string;
name?: string;
}
What I was expecting the URI in the above object will be transformed to string like (file:///index.js) and it will be a plain object like below.
const data = {
children: {
resource: "///file://tmp"
children: {
resource: "///file:/index.js"
}
}
Here is the solution, I came up with. But I would like to see others solutions as well.
const serialize = (stat: IFileStat) => Object.keys(stat).reduce((result, name) => {
// console.log(result);
if (name === 'resource') {
return result = { ...stat, resource: stat[name].toString() };
} else if (name === 'children') {
return { ...result, ...{ children: stat[name].map(child => serialize(child as any)) } }
}
else {
return result
}
}, {});

Resources