aar*_*ard 4 generics typescript
(打字稿新手警告)
我正在创建一个可重用的 reducer,它接受一个状态和一个动作并返回该状态,但仅限于接受在给定键处包含某种类型的状态。此键作为函数的参数传递。如果传递的状态对象不包含传递的键,则编译器应引发错误。
现在,我得到了这个工作。然而,在我看来,编译器生成的错误消息并不能充分描述问题。它没有说“类型上缺少 x 属性”,而是给出了其他错误,我将在下面详细说明。
// FetchAction up here, not so relevant...
export type FetchState = {
status: FetchAction,
timestamp: Date
} | null
export type LoginState = {
token: string | null,
fetching: FetchState
};
Run Code Online (Sandbox Code Playgroud)
const intialState: LoginState = {
token: null,
fetching: null
}
const loginReducer: Reducer<LoginState> = (state = intialState, action) => {
//...other operations
return fetchingReducer(state, action, 'fetching');
}
Run Code Online (Sandbox Code Playgroud)
type FetchContainingState<S, K extends keyof S> = {
[F in keyof S]: F extends K ? FetchState : S[F];
};
export const fetchingReducer = <S extends FetchContainingState<S, K>, K extends keyof S>(state: S, action: Action, key: K): S => {
// implementation
}
Run Code Online (Sandbox Code Playgroud)
这工作正常。如果我将函数调用更改为:(
return fetchingReducer(state, action, 'fetchin');拼写错误fetching),则会出现此错误:
“LoginState”类型的参数不可分配给“FetchContainingState”类型的参数。属性“令牌”的类型不兼容。输入'字符串| null' 不可分配给类型 'FetchState'。类型 'string' 不可分配给类型 'FetchState'。
好吧,它给我一个错误很好。但是,它只是警告我关于"token",或对象上存在的任何其他属性。它没有给我任何关于它期望但不存在的属性的直接指示。
type EnsureFetchState<S, K extends keyof S> = S[K] extends FetchState ? S : never;
export const fetchingReducer = <S, K extends keyof S>(state: EnsureFetchState<S, K>, action: Action, key: K): S => {
// implementation
}
Run Code Online (Sandbox Code Playgroud)
这也有效,当我将呼叫更改为return fetchingReducer(state, action, 'fetchin');(拼写错误"fetching")时,我得到:
“LoginState”类型的参数不可分配给“never”类型的参数。
更简洁,但对错误的描述更少。它对传递的参数可能有什么问题给出了更少的指示。
在方法 1 中,我使用了映射类型,在方法 2 中,我使用了条件类型来确定我传递的state和的值key没有通过我们正在搜索的条件。但是,在这两种情况下,错误消息都没有真正描述真正的问题是什么。
我是 Typescript 中更高级类型的新手,所以可能有一种非常简单的方法可以做到这一点,或者我忽略了一个简单的概念。但愿如此!但无论如何,其要点是:如何以更惯用的方式对具有动态键的对象进行这种类型检查,或者以编译器生成更有益的错误消息的方式进行?
在描述类型约束时,尽可能直接通常会有所帮助。S具有K以 type命名的属性的类型的约束FetchState不需要提及其他属性:
export const fetchingReducer = <S extends {[k in K]: FetchState}, K extends string>(state: S, action: Action, key: K): S => {
Run Code Online (Sandbox Code Playgroud)
这似乎产生了所需的错误消息,至少在这个示例代码中(我只是为缺少的类型编写了定义以使其完整):
export interface Action {
a: string;
}
export interface FetchAction extends Action {
f: string;
}
export type FetchState = {
status: FetchAction,
timestamp: Date
} | null
export type LoginState = {
token: string | null,
fetching: FetchState
};
const intialState: LoginState = {
token: null,
fetching: null
}
export type Reducer<S> = (s: S, a: Action) => S;
const loginReducer: Reducer<LoginState> = (state = intialState, action) => {
fetchingReducer(state, action, 'fetching'); // Argument of type 'LoginState'
// is not assignable to parameter of type '{ fetching: FetchState; }'.
// Property 'fetching' is missing in type 'LoginState'.
fetchingReducer(state, action, 'token'); // Argument of type 'LoginState' is
// not assignable to parameter of type '{ token: FetchState; }'.
// Types of property 'token' are incompatible.
// Type 'string | null' is not assignable to type 'FetchState'.
// Type 'string' is not assignable to type 'FetchState'.
// OK
return fetchingReducer(state, action, 'fetching')
}
export const fetchingReducer = <S extends {[k in K]: FetchState}, K extends string>(state: S, action: Action, key: K): S => {
return {} as S;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1597 次 |
| 最近记录: |