Typescript - Type Guard Issue - Make some properties required

91 views Asked by At

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.
}

1

There are 1 answers

0
jsejcksn On BEST ANSWER

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.)