javascript - Çalışma zamanında fonksiyon ve yapıcıyı ayırt et


TypeScript'te, bir Error fabrikasını argüman olarak alan bir fonksiyon yazıyorum: ya bir sınıf adı ya da bir fabrika fonksiyonu. Aşağıdaki gibi bir şey:

// Alias from class-transformer package
type ClassConstructor<T> = {
  new (...args: any[]): T;
};

function doSomething(value: number, errorFactory: () => Error | ClassConstructor<Error>) {
  if (value === 0) {
    // Zero is not allowed
    if (/* errorFactory is a class constructor */) {
      throw new errorFactory()
    } else {
      throw errorFactory()
    }
  }
}

Yukarıdaki işlevde, errorFactory bir Error sınıfı olabilir, böylece aşağıdaki kod çalışır:

doSomething(0, Error);

Veya bir Error oluşturan bir fonksiyon olabilir:

doSomething(0, () => new Error());

Sorun, ClassConstructor bir TypeScript türü olduğu için, JavaScript'e derleme sırasında hayatta kalmaması.

typeof(Error) function'dir ve typeof(()=>{}) de öyledir.

Peki bir parametrenin türünü nasıl belirleriz? /* errorFactory bir sınıf yapıcısıdır */ içinde test ne olmalı?

Cevap #1

Yukarıdaki koda göre, yapıcıyı new ile çağırmanız gerektiğini ve fonksiyonu new olmadan çağırmanız gerektiğini anladım.

Yukarıdaki örnekte, Error aslında new olmadan çağrılabilir, sadece yazarak şöyle çağrılabilir:

throw Error(msg); // msg: string = The error message

Aşağıdaki kod, bir fonksiyonun (veya yapıcının) new ile çağrılması gerekip gerekmediğini test etmek için kullanılabilir:

// Returns:
// - True if the 'func' must be called with 'new'
// - False if the 'func' can be called without 'new'
function isConstructor(func) {
  try {
    // Invoke without 'new'
    func();
  }
  catch (err) {
    if (/^TypeError:.*?constructor/.test(err.toString())) {
      // The error is about that it isn't a normal function
      return true;
    }
    else {
      // The error isn't about that; let's throw it
      throw new err.constructor(err.toString().substr(err.toString().indexOf(" ") + 1), {cause: err});
    }
  }
  return false;
}

// Let's run some tests
console.log(isConstructor("abc".constructor)); // False
// String() returns empty string; it's not a constructor
console.log(isConstructor(() => {}));          // False
// Arrow function
console.log(isConstructor(class {}));          // True
// This *is* a cliss
console.log(isConstructor(Image));             // True
// Some built-in cannot be invoked without 'new'
console.log(isConstructor(throwErr));          // TypeError: Another error
function throwErr() {
  throw new TypeError("Another error");
}