Search code examples
typescriptamazon-web-services

how do you use Partial<Type>?


I am trying to write a test for an aws-lambda function, and simply want to define an object that I can use to test the authorization context.

So I am trying:

  let event: Partial<APIGatewayProxyEvent>
  event = {
    requestContext: { 
      authorizer: {
        lambda: {
          'permissions': ['general'],
        }
      }
    }
  }

Typescript still yells at me telling me it is missing properties...

I thought a partial means it is acceptable to only have part of the required attributes?


Solution

  • The Partial<T> utility type only makes the properties of T optional. That means each property can be missing/undefined, or it can be fully present. So Partial<{a: {b: string}}> is equivalent to {a?: {b: string}}. You can satisfy it with {a: {b: ""}} or with {}. But you can not satisfy it with {a: {}}, because {} is not a value of type {b: string}.

    You're looking for a DeepPartial<T> type that recurses down into each property of T and makes it optional all the way down. You'll need to write such a type yourself or import a library that exposes one. TypeScript generally does not provide utility types unless such types are required for TypeScript to function (e.g., if --declaration sometimes makes .d.ts files that have to refer to them). Different people have different ideas about what constitutes correct behavior in the face of edge cases, and TypeScript does not want to choose between such behaviors unless it needs to.

    The "naive" implementation of DeepPartial looks like

    type DeepPartial<T> = { [K in keyof T]?: DeepPartial<T[K]> }
    

    and it works for many but not all use cases. In particular, the compiler might balk at allowing something to be assignable to DeepPartial<any>. And if T is an array type, DeepPartial<T> will be an unfortunate thing which allows you to, for example, not have the push() method. That is, you don't want DeepPartial<{a: string}[]> to look like Partial<Array<{a?: string}>>. Instead, you probably want it to look like {a?: string}[]. That leads us to the recursive conditional version here:

    type DeepPartial<T> =
        T extends readonly any[] ? { [I in keyof T]: DeepPartial<T[I]> } :
        T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } :
        T
    

    That works for your example:

    let event: DeepPartial<APIGatewayProxyEvent>
    event = {
        requestContext: {
            authorizer: {
                lambda: {
                    'permissions': ['general'],
                }
            }
        }
    }
    

    Playground link to code