typescriptgraphql

Is there anyway to do nested Pick<> types in Typescript


So I am trying to get safety on my client side GraphQL queries (so if there is a better approach to this let me know).

But what I have been doing is defining my query like this.

export const tenantManagePageQuery = async (tenantId: string) =>
    graphQLClient.request<{
        tenants: TenantManagePageQueryTenant[];
    }>(
        /* GraphQL */ `
            query tenants($tenantId: String!) {
                tenants(tenantIds: [$tenantId]) {
                    id
                    description
                    name
                    approvedUsers {
                        id
                        alias
                    }
                    pendingUsers {
                        id
                        alias
                    }
                }
            }
        `,
        { tenantId },
    );

in order to define the TenantManagePageQueryTenant type I do something like this

interface TenantManagePageQueryTenant
    extends Pick<Tenant, 'id' | 'description' | 'name'> {}

Where the base Tenant model is my GQL model type.

Is there anyway to do this kind of Pick statement but to also pick the nested properties.

something like

interface TenantManagePageQueryTenant
    extends Pick<Tenant, 'id' | 'description' | 'name' | Pick<approvedUser| 'id' | 'alias'> {}

Solution

  • Similar to Colins answer, but coming at it from the opposite direction. If you have an existing interface/type you need to pick apart you can use indexes:

    
    // Existing type
    type Tenant = {
        id:string;
        description:string;
        name:string;
        approvedUsers: Array<{
          id:string;
          alias:string;
        }>
    }
    
    // Pick it apart
    type TenantManagePageQueryTenant = 
      Pick<Tenant, 'id' | 'description' | 'name'> & {
        approvedUsers: Array<Pick<Tenant['approvedUsers'][0], 'id' | 'alias'>>
      }
    

    Alternative 1 - Extracted types

    As noted in the comments, this can be slightly cleaner:

    type TenantSubset = Pick<Tenant, 'id' | 'description' | 'name'>
    type ApprovedUserSubset = Pick<Tenant['approvedUsers'][number], 'id' | 'alias'>
    
    type TenantManagePageQueryTenant = TenantSubset & { approvedUsers: Array<ApprovedUserSubset> }
    

    Alternative 2 - Pick-less pick

    This version, while a little more repetitive, is (subjectively) easier to read:

    type TenantManagePageQueryTenant = { 
      id: Tenant['id'],
      description: Tenant['description'],
      name: Tenant['name'],
      approvedUsers: Array<{
        id: Tenant['approvedUsers'][number]['id'],
        alias: Tenant['approvedUsers'][number]['alias'],
      }> 
    }
    
    

    Playground