Is there a way with ts-toolbelt to recursively add a specific property to all nested objects of a given type?
Example of what I am trying to do:
interface Address {
city: string
country: string
}
interface User {
id: string
name: string
address: Address
}
interface Session {
id: string
user: User
}
Convert this to:
interface SessionTypenames {
session: {
__typename: string // new property
id: string
user: {
__typename: string // new property
id: string
name: string
address: {
__typename: string // new property
city: string
country: string
}
}
}
This would be very helpful in defining mocked data from server when using Apollo's MockedProvider which requires adding this data to the mocked data, but not defined by the original graphql schema types.
There is no magic solution. If you'd like to do this generically, you'd have to write a custom type.
import {O} from "ts-toolbelt";
interface Address {
city: string
country: string
}
interface User {
id: string
name: string
address: Address
}
interface Session {
id: string
user: User
}
type NestedSession = { // we nest like suggested
session: O.MergeUp<Session, {
user: O.MergeUp<User, {
address: Address
}, 'deep'>
}, 'deep'>
}
type AddTypeNames<O extends object> = {
[K in keyof O]: O[K] extends object
// if it is an object then recursive call to add field
? AddTypeNames<O.MergeUp<O[K], {__typename: string}>>
: O[K] // if not, then we'll leave the field untouched
} & {} // <- force to display neatly (try to remove this to see)
type SessionTypenames = AddTypeNames<NestedSession>
@JeremyJonas do you need a generic type to do this for you?
@pirix-gh Manual nesting (NestedSession) doesn't seem necessary - see this code snippet:
import {A} from 'ts-toolbelt'
interface Address {
city: string
country: string
}
interface User {
id: string
name: string
address: Address
}
interface Session {
id: string
user: User
}
type AddTypeNames<O> =
O extends object
// A.Compute here isn't necessary, but produces nicer readable types
? A.Compute<{__typename: string} & {[K in keyof O]: AddTypeNames<O[K]>}>
: O;
type SessionTypenames = AddTypeNames<Session>;
Feel free to use this in any project - the above code snippet is free software released into the public domain.
Yes you're right, I somehow missed the types were already nested :man_facepalming:
type AddTypeNames<O> = O extends object // A.Compute here isn't necessary, but produces nicer readable types ? A.Compute<{__typename: string} & {[K in keyof O]: AddTypeNames<O[K]>}> : O;
This works perfectly. Exactly what I was looking for!
Thank you!
type AddTypeNames<O> = O extends object // A.Compute here isn't necessary, but produces nicer readable types ? A.Compute<{__typename: string} & {[K in keyof O]: AddTypeNames<O[K]>}> : O;
This works perfectly. I had to actually remove the A.Compute because that was causing the outcome to be any, while removing it output correctly nested types as expected.
Thank you!
@JeremyJonas do you need a generic type to do this for you?
This works great, thanks @pirix-gh! Exactly what I was looking for (minus the additional nested type as mentioned ;)
Most helpful comment
@pirix-gh Manual nesting (
NestedSession) doesn't seem necessary - see this code snippet:Feel free to use this in any project - the above code snippet is free software released into the public domain.