網站搜尋

types - 可用於函數聲明的函數回調的 TypeScript 類型定義


是否有可能在 Typescript 中為回呼函數打上類型,以在型別系統中編碼以下兩個不變式:

  1. 如果 err == null,則 x 的類型是 T
  2. 如果 err != null,則 x 不應被引用 (從不?)
type Callback<T> = {
  (error: null, value: T): void;
  (error: Error): void;
};

// The function that takes the typed callback.
function incrementAsync(x: number, cb: Callback<number>): void {
  if (x === 0) {
    return cb(new Error());
  }

  cb(null, x + 1); // should type-check
  cb(new Error()); // should type-check

  cb(new Error(), 1) // should fail type-check
  cb(null);          // should fail type-check
}

這是我真正想要的函數簽名:

// Error: This overload signature is not compatible 
//        with its implementation signature.
function definedCb1(e: Error);
function definedCb1(e: null, x: number): void {}
incrementAsync(10, definedCb1); // error with strictNullChecks

這裡有一個較寬鬆的函數簽名,可以進行類型檢查

// BAD because types are too loose but this passes type-check.
function definedCb2(e: Error|null, x?: number): void {
  if (e === null) {
    // Is there a way to type this function, so the ! is not necessary?
    console.log(x! + 1);
    return;
  }
  // error because x might be undefined.
  console.log(x + 2);
}
incrementAsync(20, definedCb2) // Works

Typescript Playground 連結啟用 strictNullChecks 和 noImplicitAny。

答案#1

如果你啟用 strictNullChecks,那麼這就符合你的需求。

interface Callback<T> {
  (error: Error): void;
  (error: null, value: T): void;
}

function incrementAsync(x: number, cb: Callback<number>): void {
  cb(new Error());          // works
  cb(null, x + 1);          // works

  cb(null);                 // error
  cb(new Error(), 'foo');   // error
  cb(null, 'foo');          // error
  cb(new Error(), 10);      // error    
}

這裡在遊樂場。

strictNullChecks 標誌防止所有類型成為可為 null 的類型。