Tìm kiếm trang web

typescript - kiểm tra kiểu hoạt động với `if` kiểm tra bên trong thân hàm nhưng không thông qua một lời gọi hàm với cùng một kiểm tra


Xem xét MWE sau đây:

function print (x: number) {
  console.log(x)
}

function noString (a: number | string) {
  if (typeof a === 'string') {
    throw new Error('error')
  }
}

export function funOne () {
  const a: number | string = 'abc'

  if (typeof a === 'string') {
    throw new Error('error')
  }

  print(a)
}

export function funTwo () {
  const a: number | string = 'abc'

  noString(a)

  print(a)
}

Tôi nghĩ funOne và funTwo về cơ bản là giống nhau. Tuy nhiên, tôi nhận được [tsserver 2345] [E] Argument of type 'string' is not assignable to parameter of type 'number'. ở dòng cuối cùng trong funTwo.

Tại sao điều này xảy ra và tôi nên tránh điều này như thế nào?

Câu trả lời #1

Điều này xảy ra vì TypeScript không biết rằng noString được sử dụng như một kiểm tra kiểu. Nhưng bạn có thể thông báo cho TypeScript về điều đó, ví dụ như sau:

function noString(a: number | string): number {
  if (typeof a === 'string') {
    throw new Error('error');
  }
  return a;
}

export function funTwo() {
  const a: number | string = 'abc';
  print(noString(a));
}

Hoặc bạn có thể sử dụng một dấu kiểm tra kiểu:

function noString(a: string | number): a is number {
    if (typeof a === 'string') {
        throw new Error('error');
    }
    return true;
}

export function funTwo() {
    const a: number | string = 'abc';
    if (noString(a)) {
        print(a);
    }
}

Như đã đề cập trong các bình luận của @Artyer và @futur, bạn cũng có thể sử dụng assert:

function noString(a: string | number): asserts a is number {
    if (typeof a === 'string') {
        throw new Error('error');
    }
}

export function funTwo() {
    const a: number | string = 'abc';
    noString(a)
    print(a);
}

Câu trả lời #2

Điều này xảy ra vì hàm print của bạn mong đợi một số nhưng lại nhận được một chuỗi trong funTwo. Khi gọi hàm print, biến là một chuỗi vì bạn đã khởi tạo nó với 'abc' và TypeScript không thể nhận ra rằng noString làm vô hiệu kiểu chuỗi.

funOne không sao vì trước khi đến hàm print, bạn đã loại trừ trường hợp biến a là một chuỗi bằng cách ném ra một lỗi. Phần còn lại của mã do đó sẽ không thể truy cập được nếu biến có kiểu là chuỗi. Vì vậy, khi vượt qua điều kiện if (typeof a === 'string'), biến a chỉ có thể là một số, vì vậy TypeScript sẽ không báo lỗi với bạn.

Giải pháp

Nếu bạn chắc chắn về kiểu của funTwo, bạn có thể ép kiểu nó như sau:

  print(a as unknown as number)