[英]Typescript overloading functions with union types
我希望有一個函數,它接受兩個聯合類型作為參數,但強制它們成為相同的子類型...而不必在調用函數之前親自檢查類型。
這是我嘗試過的:
背景故事
為了簡單起見,我將定義一些類型:
type Foo = number;
type Bar = [number, number?];
type FooBar = Foo | Bar;
我有一個功能:
function fn(x: FooBar, y: FooBar) {}
我想強制x
和y
為同一類型。 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
}
反正有做我想做的事嗎? 也許使用不同的模式或方法。
在調用fn(x, y)
之前,必須進行某種類型檢查,因為所有編譯器都知道x
和y
均為foobar
類型,並且已聲明fn
,使其僅接受兩個foo
參數或兩個bar
參數,而不是兩者都選。 您知道x
和y
都將是number
(aka foo
),但這是因為您已經完成了JSON.parse()
的工作,並得出結論認為類型可以。 編譯器不是那么聰明。 就其所知, x
可能是foo
, y
可能是bar
,然后fn(x, y)
不起作用。 那就是: fn(x,y)
沒有類型檢查的錯誤是一個很好的錯誤,可以幫助您編寫更好的代碼。
因此,在x
和y
上調用重載的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 😠
}
如您所見,它不起作用。 正確處理聯合類型的呼叫簽名是一個懸而未決的問題 。 特別是, x
和y
的類型是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
可以安全地應用於x
和y
,即使在通常情況下也不能。 請注意,作為斷言,您放棄了編譯器的安全性,以換取能夠隨意編寫所需代碼的自由。
就我個人而言,我可能會離開冗余類型檢查,然后繼續進行。 它具有編譯器強制類型安全的優點。 但這取決於你。 希望能有所幫助; 祝好運!
這里的問題是,在您提供的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
類型,現在該類型始終與foo
和bar
匹配,就好像它們可以互換一樣。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.