簡體   English   中英

函數參數的打字稿通用約束

[英]Typescript generic constraint for function parameter

編輯:將界面中的“測試”固定為“約束”

我無法理解通用約束在 Typescript 中是如何工作的。 我正在使用 React/Redux,您可以在其中找到與此類似的構造:

interface SomeConstraint { constraint: any }
type SomeType<T> = <A extends SomeConstraint>(item: T, action: A) => void;

如您所見,這是一個帶有通用參數和一些約束的函數定義。 現在,當我想使用它時,我可以編寫以下內容:

interface Test { constraint: string }
const some : SomeType<string> = (s: string, c: Test ) => {}

這很好用,但是,當我想擴展測試接口時(我們稱之為 TestWithData):

interface TestWithData  { constraint: string, payload: any }
const some : SomeType<string> = (s: string, c: TestWithData ) => {}

我收到編譯時錯誤:

錯誤 TS2322:類型 '(s: string, c: TestWithData) => void' 不可分配給類型 'SomeType'。 參數“c”和“action”的類型不兼容。 類型“A”不可分配給類型“TestWithData”。 類型“SomeConstraint”不可分配給類型“TestWithData”。 “SomeConstraint”類型中缺少屬性“test”。

我缺少什么?


已編輯

正如我所說,我在 redux 類型中發現了這個(類似的)構造。 你可以找到這個定義(redux "3.7.2"):

export type Reducer<S> = <A extends Action>(state: S, action: A) => S;

現在當你想定義你的減速器時,你需要提供狀態和動作,我將跳過狀態部分,所以,我可以寫這樣的動作:

interface MyAction { type: "MY_ACTION" }

創建減速器很容易:

const reducer: Reducer<MyState> = (state: MyState, action: MyAction) => {...}

這將通過編譯,但如果我向 MyAction 添加其他數據,如下所示:

interface MyAction { 
    type: "MY_ACTION";
    payload: any
}

編譯將因上述錯誤而失敗。 我懷疑它會通過。 那么這個約束的目的是什么? 告訴編譯器我們期望完全相同的類型(結構上)並且僅此而已? 我認為這是一個非常有限的用例,我希望類型推斷會選擇類型,檢查它的結構兼容性並保留類型簽名。 像這樣但不需要指定類型參數:

export type Reducer<S, A extends Action> = (state: S, action: A) => S;

現在,類型簽名被保留,但我們需要在聲明變量時指定實際參數類型(與我們對狀態所做的相同):

const reducer: Reducer<MyState, MyAction> = (state: MyState, action: MyAction) => {...}

對於你給的例子,我不認為通用+約束是我定義的類型(它很可能是我會怎么做它標稱的類型系統,而不是在一個結構類型系統)的方式。

type SomeType<T> = <A extends SomeConstraint>(item: T, action: A) => void;

如果您說“該項目必須具有constraint屬性,則可以使用更簡單的方法來獲得它:

type SomeType<T> = (item: T, action: SomeConstraint) => void;

結構類型負責其余的工作。

這是我的更新版本,因為之前的答案在 TypeScript 3.8.3 中停止工作。 我已將約束設為通用,因為約束將與項目具有相同的類型。 因為可以推斷參數類型,所以我們不需要顯式聲明它們(即我們不需要寫三次string ......像這樣const actualImplementation: SomeType<string> = (item: string, action: string): void => { } - 我們可以只寫一次作為類型參數。

interface SomeConstraint<T> {
    constraint: T
}

type SomeType<T> = (item: T, action: SomeConstraint<T>) => void;

const actualImplementation: SomeType<string> = (item, action): void => { }

const a: SomeConstraint<string> = { constraint: '' };

actualImplementation('', a);

上面示例中的關鍵點是實際實現可以是您的Test類型,沒有錯誤。

暫無
暫無

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

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