Convert Object<T> to Object<A> where A is contained in T

570 views Asked by At

I'm working with Prisma to generate schemas and models for my database data.
There, I define a "User" model, which has this data (the type is automatically generated by Prisma):

type User {
    name: string,
    nickName: string,
    sensitiveInfo1: string,
    sensitiveInfo2: string
}

However, when my client requests this data, I can't send the sensitive info in the response payload.

Prisma has a very handy way for defining a custom type:

import { UserGetPayload } from "./prisma/src/generated/client"

// now I define a custom type
export type UserPublicInfo = UserGetPayload<{
    select: {
        name: true,
        nickName: true,
    }
}>

// this gives me the type:
// type UserPublicInfo {
//        name: <whatever type name is in the schema>,
//        nickName: <whatever type nickName is in the schema>
// }

Now, suppose I already have a user instance retrieved from the database, of type User. I wan't to cast it to "UserPublicInfo" in a way that the response payload only contains info of "UserPublicInfo".

If I cast an Object of type User like user as UserPublicInfo the type suggestions points to the right direction, showing only attributes of the subtype. However, the sensitive data still there.

I'm junior at javascipt/typescript but I believe this has something to do with the Object's prototype. So how can I cast it this way?

1

There are 1 answers

2
Linda Paiste On BEST ANSWER

There's two pieces to this question, how to tell typescript that the properties are gone and how to actually remove them.

Typescript

What you are looking for are the typescript utilities Pick and Omit.

To get only the fields name and nickName from User:

type UserPublicInfo = Pick<User, 'name' | 'nickName'>

To get all fields from User except for sensitiveInfo1 and sensitiveInfo2:

type UserWithoutPrivate = Omit<User, 'sensitiveInfo1' | 'sensitiveInfo2'>

In the example you've given, both of these types are identical and resolve to

type UserPublicInfo = {
    name: string;
    nickName: string;
}

Javascript

Now for removing the properties. The easiest thing is to use the pick and omit functions from the package lodash. Those have built-in typescript support so you don't need to know about any of the TS stuff I just told you.

If you want to do it yourself, you can use the spread operator to drop the properties that you don't want (note: some linters will complain about unused variables here).

export const userToPublic = (user: User): UserPublicInfo => {
    const {sensitiveInfo1, sensitiveInfo2, ...rest} = user;
    return user;
}

Typescript Playground Link