簡體   English   中英

具有聯合類型的Typescript重載函數

[英]Typescript overloading functions with union types

我希望有一個函數,它接受兩個聯合類型作為參數,但強制它們成為相同的子類型...而不必在調用函數之前親自檢查類型。

這是我嘗試過的:

背景故事

為了簡單起見,我將定義一些類型:

type Foo = number;
type Bar = [number, number?];
type FooBar = Foo | Bar;

我有一個功能:

function fn(x: FooBar, y: FooBar) {}

我想強制xy為同一類型。 IE允許fn(Foo, Foo)fn(Bar, Bar)但不允許fn(Foo, Bar)fn(Bar, Foo) 因此,我決定重載該功能。

function fn(x: Foo, y: Foo): void;
function fn(x: Bar, Bar): void;
function fn(x: FooBar, y: FooBar): void {}

我從API請求數據。 我從API獲得了兩個對象,我懷疑這是否重要,但是它們將是同一類型,都是Foo或都是Bar 這取決於我的要求。

問題

當我調用函數時,它會抱怨(我使用JSON.parse作為模擬API調用的一種方式):

const x: FooBar = JSON.parse("1");
const y: FooBar = JSON.parse("2");
fn(x, y);

無法將類型'FooBar'的參數分配給類型'[number,number?]'的參數。 類型“數字”不可分配給類型“ [數字,數字?]”。

如果我事先檢查類型,它會起作用:

(旁注:我希望在使用聲明的類型時,有一種更簡單的方法來檢查類型)

if(typeof x === 'number' && typeof y === 'number') fn(x, y);
else if(Array.isArray(x) && Array.isArray(y)) fn(x, y);

但這似乎很愚蠢,我不想在調用該函數之前檢查類型,而最終調用將完全相同。

有點令人驚訝地使用or不起作用(即使我仍然不想這樣做)

if((typeof x === 'number' && typeof y === 'number') ||
  (Array.isArray(x) && Array.isArray(y))) {
    fn(x, y); // same error as above
}

StackBlitz示例

反正有做我想做的事嗎? 也許使用不同的模式或方法。

在調用fn(x, y)之前,必須進行某種類型檢查,因為所有編譯器都知道xy均為foobar類型,並且已聲明fn ,使其僅接受兩個foo參數或兩個bar參數,而不是兩者都選。 知道xy都將是number (aka foo ),但這是因為您已經完成了JSON.parse()的工作,並得出結論認為類型可以。 編譯器不是那么聰明。 就其所知, x可能是fooy可能是bar ,然后fn(x, y)不起作用。 那就是: fn(x,y)沒有類型檢查的錯誤是一個很好的錯誤,可以幫助您編寫更好的代碼。

因此,在xy上調用重載的fn的最直接方法是您在此處進行的類型檢查:

if (typeof x === 'number' && typeof y === 'number') fn(x, y);
else if (Array.isArray(x) && Array.isArray(y)) fn(x, y);

是的,這是多余的。 嘗試將它們折疊為一個語句:

if ((typeof x === 'number' && typeof y === 'number') ||
  (Array.isArray(x) && Array.isArray(y))) {
  fn(x, y); // error 😠
}

如您所見,它不起作用。 正確處理聯合類型的呼叫簽名是一個懸而未決的問題 特別是, xy的類型是foobar相關子類型(這意味着它們都是foo或都是bar ),但是編譯器無法處理。 我已經經歷了無數次,甚至提出了一種處理此問題的方法 (可憐的我,它不會被采用),但是目前還沒有很好的解決方案。

如果您不想要冗余的運行時代碼(以換取一些編譯時的混亂),則可以在此處執行的最簡單的操作是使用類型斷言

if ((typeof x === 'number' && typeof y === 'number') ||
  (Array.isArray(x) && Array.isArray(y))) {
  (fn as (x: foobar, y: foobar) => void)(x, y); // okay
}

}

在這里,您斷言在類型檢查塊中, fn可以安全地應用於xy ,即使在通常情況下也不能。 請注意,作為斷言,您放棄了編譯器的安全性,以換取能夠隨意編寫所需代碼的自由。

就我個人而言,我可能會離開冗余類型檢查,然后繼續進行。 它具有編譯器強制類型安全的優點。 但這取決於你。 希望能有所幫助; 祝好運!

這里的問題是,在您提供的StackBlitz示例中,您的fn(foobar,foobar)仍將對fn(foo,bar)執行。 在編輯器中報告了一個錯誤,但是如果將console.log放在fn的正文中,則會看到打印出來。 如果傳遞fn(1, [2, 3]) ,您會看到它打印出來沒有問題。

為了在此處將使用限制為特定的匹配,您可以進行類型檢查。

type foo = number;
type bar = [number, number?];
type foobar = foo | bar;

function fn(x: foobar, y: foobar): void {
  if (typeof x != typeof y) return;
  console.log(x + " __ " + y);
}

const x: foobar = JSON.parse("1");
const y: foobar = JSON.parse("2");

// Executes
fn(x, y);
// Doesn't execute
fn(2, [4, 5]);

之所以會出現問題,是因為您要定義foobar類型,現在該類型始終與foobar匹配,就好像它們可以互換一樣。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM