Nghiệp
Nghiệp

Reputation: 4718

Type Guard Typescript not inferring function rest parameters correctly

I have an example of Typescript with type guard and function rest parameters:

interface BaseProps {
  username: string;
  password: string;
}

type Props = BaseProps & (
  | {isAdmin: true, adminName: string} 
  | {isAdmin: false}
)


// Doesn't works
const myFn = ({isAdmin, ...rest}: Props) => {
  if(isAdmin === true) {
    rest.adminName; // Property 'adminName' does not exist on type '{ username: string; adminName: string; } | { username: string; }'.
  }
}

// It works
const myFn2 = (args: Props) => {
  if(args.isAdmin === true) {
    args.adminName;
  } 
}

What is wrong with the rest parameters and type guard?

TS Playground


UPDATE SOLUTION:

I found the solution, using Assert Functions to resolve the issue.

declare function assertType<T>(val: unknown): asserts val is T;

const myFn = ({password, isAdmin, ...rest}: Props) => {

  if(isAdmin === true) {
    
    assertType<Omit<Props & {isAdmin: true}, keyof Props>>(rest);

    rest.adminName; // <=== HERE

    rest.username;

    // Should be error
    rest.password;
  }
}

TS Playground

Upvotes: 0

Views: 212

Answers (1)

known-as-bmf
known-as-bmf

Reputation: 1222

The type of Props is

{ username: string, isAdmin: true, adminName: string } | { username: string, isAdmin: false }

When you assert the value of isAdmin on an object of type Prop (e.g. an if statement), typescript is then able to narrow down the actual type between { username: string, isAdmin: true, adminName: string } and { username: string, isAdmin: false }.

If you remove isAdmin by destructuring, the type of rest becomes

{ username: string, adminName: string } | { username: string }

The values isAdmin and rest become uncorrelated and the only way to narrow down the type of rest is to assert the presence of the adminName property.

Maybe you shouldnt use rest parameters for this specific case. You could also manually cast the rest variable inside the if.

Upvotes: 0

Related Questions