typescripttypeguards

Typescript - Type Guard Issue - Make some properties required


I want to make the p1 property a required property when it passes typeGuard, but I have no idea how to define the red square box. Does anyone know? enter image description here

typescript code is below

type T = {p1?: string; p2?:number; p3?: boolean}
const t: T = {p1: '1'};

function typeGuard<T extends object, Key extends keyof T>(t: T, key: Key): t is T & {[K in keyof T]: T[K]} {
  return t[key] !== undefined;
}

if (typeGuard(t, 'p1')){
  t.
}


Solution

  • You can trivially implement a generic, user-defined type guard function to verify the presence of a key in an object by using a few built-in type utilities:

    The function looks like this:

    function hasKey<K extends PropertyKey, T extends Partial<Record<K, any>>>(
      obj: T,
      key: K,
    ): obj is T & Required<Pick<T, K>> {
      return key in obj;
    }
    

    Using it with the example data in your question looks like this:

    type Example = {
      p1?: string;
      p2?: number;
      p3?: boolean;
    };
    
    const example: Example = { p1: "1" };
    
    if (hasKey(example, "p1")) {
      example
    //^? const example: Example & Required<Pick<Example, "p1">>
    
      example.p1
            //^? (property) p1: string
      example.p2
            //^? (property) p2?: number | undefined
      example.p3
            //^? (property) p3?: boolean | undefined
    }
    

    However, I'm not sure there's a great deal of value in such a function. The idiomatic inline expression — which uses the in operator — is syntactically shorter and produces the same result:

    See in the TS Handbook: The in operator narrowing

    if ("p1" in example) {
      example
    //^? const example: Example & Required<Pick<Example, "p1">>
    
      example.p1
            //^? (property) p1: string
      example.p2
            //^? (property) p2?: number | undefined
      example.p3
            //^? (property) p3?: boolean | undefined
    }
    

    Code in TypeScript Playground


    Note that things become a bit more complicated if undefined is a union member for the optional value. Be sure to read about the compiler option noUncheckedIndexedAccess to understand more. (The playground link above is configured with that option enabled.)