我正在遵循Kent Dodds提出的React 動作/還原器模式,我正試圖為其添加一些型別安全。
export type Action =
{ type: "DO_SOMETHING", data: { num: Number } } |
{ type: "DO_SOMETHING_ELSE", data: { nums: Number[] }; { nums: []. };
型別 Actions = {
[key in Action["type"/span>]]。(state。State, data: Action["data"]) => State。
};
const actions: Actions = {
DO_SOMETHING: (state, data) => {
return { nums: [data.num] }; // Type error
},
DO_SOMETHING_ELSE: (state, data) => {
return { nums: data.nums }; // type error }
}
};
這段代碼很好,因為它確保了actions物件包含Action聯盟型別中列出的所有動作型別,并且在嘗試調度一個動作時提供了型別安全。當試圖訪問data的成員時,問題就來了。
Property 'num' 不存在于'{ num: Number; } | { nums: Number[]; }'。
屬性 'num'不存在于型別'{ nums: Number[]; }'。
但是,如果我這樣做:
export type Action =
{ type: "DO_SOMETHING", data: { num: Number } } |
{ type: "DO_SOMETHING_ELSE", data: { nums: Number[] }; { nums: []. };
型別 Actions = {
[key in Action["type"/span>]]。(state: State, action: Action) => State;
};
const actions: Actions = {
DO_SOMETHING: (state, action) => {
if (action.type !== "DO_SOMETHING" ) return state。
return { nums: [action.data.num ] }; //不再有型別錯誤。
},
DO_SOMETHING_ELSE: (state, action) => {
if (action.type !== "DO_SOMETHING_ELSE" ) return state。
return { nums: action.data.nums }; //不再有型別錯誤。
};
現在TypeScript知道action.data是匹配顯式action.type的聯合型別。有沒有一種更簡潔的方法來做到這一點,而不必將所有的動作行內到一個大的開關陳述句中?
uj5u.com熱心網友回復:
你非常接近。
這一行Action['data']在(state: State, data: Action["data"]) => State;是錯誤的。
Action['data']應該與key屬性系結在一起。
請看這個例子:
type State = {
nums: number[] 。
}
export type Action=
| { type: "DO_SOMETHING", data: { num: number } }
| { type: "DO_SOMETHING_ELSE"/span>, data: Pick<State, 'nums'> };
型別 Actions = {
[Type in Action["type"] ] 。(state。State, data: Extract<Action, { type: Type }>['data']) => State;
};
const actions: Actions = {
DO_SOMETHING: (state, data) => ({ nums: [data.num] })。)
DO_SOMETHING_ELSE: (state, data) => ({ nums: data.nums })
};
我使用了Type而不是key,因為我們是通過types屬性進行迭代。
Extract - 希望得到兩個引數。第一個引數是一個聯盟,第二個引數是它應該匹配的型別。把它當作一個Array.prototype.filter的聯合體。
P.S. 請避免使用像Number這樣的建構式型別,使用number代替。
介面Number對應于作為物件的number,Number作為類對應于類的建構式:
interface Number {
toString(radix?: number): string;
toFixed(fractionDigits?: number): string;
toExponential(fractionDigits?: number): string;
toPrecision( precision?: number): string;
valueOf(): number;
}
介面 NumberConstructor {
new(value?: any)。Number;
(value?: any): 數字。
readonly prototype: Number;
readonly MAX_VALUE: 數字。
readonly MIN_VALUE: 數字。
readonly NaN: 數字。
readonly NEGATIVE_INFINITY: 數字。
readonly POSITIVE_INFINITY: number;
}
宣告 var Number: NumberConstructor。
uj5u.com熱心網友回復:
type ActionDataMap = {
DO_SOMETHING: { num: Number };
DO_SOMETHING_ELSE: { nums:
};
型別 ActionType = keyof ActionDataMap
型別 ActionsMap = {
[K in ActionType] 。{ type: K; data: ActionDataMap[K] }
}
// This will generate the union:
型別 Action = ActionsMap[ActionType]
//你可以用K來索引ActionsMap,以找到具體的Action。
型別 Actions = {
[K in ActionType] 。(state: State, action: ActionsMap[K]) => State;
};
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/307920.html
標籤:
上一篇:<p>大家好,我需要一些幫助,讓下面的代碼發揮作用。我是按照<ahref="#"><imgsrc="https://img.codepudd
下一篇:通用物件中一個鍵的型別保護函式
