Tìm kiếm trang web

typescript - Thu hẹp kiểu trên tham số với phép kiểm tra kiểu


Tôi quan tâm đến việc thu hẹp các loại dựa trên loại của một trường duy nhất. Tuy nhiên, TypeScript dường như không sử dụng predicate kiểu để thu hẹp kiểu của các tham số khác trong kiểu giống như khi sử dụng trực tiếp các toán tử nguyên thủy trong câu lệnh if. Có cách nào để tôi có thể làm cho việc thu hẹp kiểu hoạt động chính xác ở đây không?

export function isTrue(input: boolean | undefined | null): input is true {
  return input === true;
}

type Refine =
  | {
      b: true;
      c: 'bIsTrue';
    }
  | {
      b: undefined;
      c: 'bIsUndefined';
    }
  | {
      b: false;
      c: 'bIsFalse';
    };

export function example() {
  const example = (null as unknown) as Refine;

  if (example.b === true) {
    example.b;  // Type is: true
    example.c;  // Type is: 'bIsTrue'
  }

  if (isTrue(example.b)) {
    example.b;  // Type is: true
    example.c;  // Type is: 'bIsTrue' | 'bIsUndefined' | 'bIsFalse'
  }
}

Câu trả lời #1

Một khả năng là thực hiện việc thu hẹp trên toàn bộ đối tượng, sau đó truy cập các thuộc tính của nó:

function hasTrueBProperty<T extends { b: unknown }>(object: T): object is T & { b: true } {
  return object.b === true;
}

declare const refine: Refine;
if (hasTrueBProperty(refine)) {
  refine.b;  // Type is: true
  refine.c;  // Type is: 'bIsTrue'
}

Để truyền thuộc tính vào để kiểm tra như một đối số, hãy sử dụng một generic khác:

function hasTrueProperty<P extends PropertyKey, T extends Record<P, unknown>>(object: T, prop: P): object is T & Record<P, true> {
  return object[prop] === true;
}

declare const refine: Refine;
if (hasTrueProperty(refine, 'b')) {
  refine.b;  // Type is: true
  refine.c;  // Type is: 'bIsTrue'
}

Câu trả lời #2

Thật không may, đội ngũ TypeScript đã chính thức tuyên bố rằng họ không sẵn sàng giải quyết trường hợp sử dụng này, vì lý do hiệu suất. Như một phương án thay thế, bạn có thể giới thiệu các kiểu và bộ bảo vệ kiểu ở đây. Ví dụ:

interface One {
    b: true;
    c: "bIsTrue";
}

interface Two {
    b: undefined;
    c: "bIsUndefined";
}

interface Three {
    b: false;
    c: "bIsFalse";
}

type Refine = One | Two | Three;

function isOne(refine: Refine): refine is One {
    return refine.b === true;
}

function example() {
    const example = (null as unknown) as Refine;

    if (example.b === true) {
        example.b;  // Type is: true
        example.c;  // Type is: 'bIsTrue'
    }

    if (isOne(example)) {
        example.b;  // Type is: true
        example.c;  // Type is: 'bIsTrue'
    }
}