兄弟函数的通用参数的打字稿推断
Typescript inference of sibling function's generic arguments
我想实现如下推理:
type RequestAction<Data = any, TransformedData = Data> = {
type: string;
request: any | any[];
meta?: {
getData?: (data: Data, currentData: TransformedData) => TransformedData;
[extraProperty: string]: any;
};
};
function fetchBooks(
x: number,
y: string,
): RequestAction<{ raw: boolean }, { parsed: boolean }> {
return {
type: 'FETCH_BOOKS',
request: {
url: '/books',
x,
y,
},
meta: {
getData: data => ({ parsed: data.raw }),
},
};
}
const query = useQuery({
action: fetchBooks,
variables: [1, '2'], // inferred from fetchBooks argument types
});
query.data // inferred from `fetchBooks` generic
我只能同时实现一个,而不能同时实现两个。这是我的尝试:
1)
interface QueryState<QueryStateData> {
data: QueryStateData;
error: any;
loading: boolean;
}
type Arr = readonly any[];
export function useQuery<
QueryStateData = any,
Variables extends Arr = any
>(props: {
action?: (...args: Variables) => RequestAction<any, QueryStateData>;
variables?: Variables;
}): QueryState<QueryStateData>;
这几乎可以工作,data
是根据传递给 fetchBooks: RequestAction 的 QueryStateData
自动推断出来的。变量也部分起作用,但只是部分起作用。它似乎工作正常,例如,如果 fetchBooks
参数是 (string, number)
,那么 variables
的类型确实是 [string, number]
元组。但是出于某种原因,如果我设置 variables: [1, '1']
,那么它仍然可以正常工作,因为 TS 突然输入 (string | number)[]
,将它转换为联合数组,出于我不知道的原因,这是不可接受的,我想要变量精确 action
个参数,顺序敏感。
2)
interface RequestCreator<QueryStateData> {
(...args: any[]): RequestAction<any, QueryStateData>;
}
export function useQuery<
QueryStateData = any,
R extends RequestCreator<QueryStateData> = RequestCreator<QueryStateData>
>(props: {
action?: R;
variables?: Parameters<R>;
}): QueryState<QueryStateData>;
有了这个,我可以实现完美的 variables
推理。不幸的是 query.data
将是 any
的类型,QueryStateData
推理在这种情况下由于某种原因不起作用,它不是从 action
回调泛型中选取的。
请告诉我哪种方法更正确,以及如何调整它以实现 variables
和 data
推理。或者也许还有另一种我没有考虑的方法。提前致谢!
尝试对操作使用单一类型参数,这与您的第二次尝试类似。
interface RequestCreator<QueryStateData = any> {
(...args: any[]): RequestAction<any, QueryStateData>;
}
// Gets the `QueryStateData` type from a `RequestCreator`
// (e.g. GetQueryStateData<typeof fetchBooks> is { parsed: boolean }).
type GetQueryStateData<T extends RequestCreator> = T extends RequestCreator<infer QueryStateData>
? QueryStateData
: never;
// I'm just using declare here so TS is happy
export declare function useQuery<R extends RequestCreator>(props: {
action?: R;
// The parameter types of R
// (e.g. Parameters<typeof fetchBooks> is [x: number, y: string])
variables?: Parameters<R>;
}): QueryState<GetQueryStateData<R>>;
const query = useQuery({
action: fetchBooks,
variables: [1, '2'],
});
// inferred correctly as { parsed: boolean }
query.data;
useQuery({
action: fetchBooks,
// Type 'number' is not assignable to type 'string'. (expected)
variables: [1, 1],
});
我认为(根据轶事经验;请不要在这方面引用我的话)当类型参数直接对应于参数类型(或直接“包含”在其中的类型,例如 action
).这就是编译器在您第二次尝试时无法推断 QueryStateData
类型的原因。
这是最终类型,我稍微更新了 cherryblossom 响应以仍然允许将自定义泛型传递给 useQuery:
interface RequestCreator<QueryStateData = any> {
(...args: any[]): RequestAction<any, QueryStateData>;
}
type GetQueryStateData<T extends RequestCreator> = T extends RequestCreator<
infer QueryStateData
>
? QueryStateData
: never;
export function useQuery<
Data = undefined,
QueryCreator extends RequestCreator = any
>(props: {
type: string | QueryCreator;
action?: QueryCreator;
requestKey?: string;
multiple?: boolean;
defaultData?: any;
dispatch?: boolean;
variables?: Parameters<QueryCreator>;
}): QueryState<
Data extends undefined ? GetQueryStateData<QueryCreator> : Data
> & {
load: () => Promise<{
data?: QueryState<
Data extends undefined ? GetQueryStateData<QueryCreator> : Data
>;
error?: null;
isAborted?: true;
action: any;
}>;
};
我想实现如下推理:
type RequestAction<Data = any, TransformedData = Data> = {
type: string;
request: any | any[];
meta?: {
getData?: (data: Data, currentData: TransformedData) => TransformedData;
[extraProperty: string]: any;
};
};
function fetchBooks(
x: number,
y: string,
): RequestAction<{ raw: boolean }, { parsed: boolean }> {
return {
type: 'FETCH_BOOKS',
request: {
url: '/books',
x,
y,
},
meta: {
getData: data => ({ parsed: data.raw }),
},
};
}
const query = useQuery({
action: fetchBooks,
variables: [1, '2'], // inferred from fetchBooks argument types
});
query.data // inferred from `fetchBooks` generic
我只能同时实现一个,而不能同时实现两个。这是我的尝试:
1)
interface QueryState<QueryStateData> {
data: QueryStateData;
error: any;
loading: boolean;
}
type Arr = readonly any[];
export function useQuery<
QueryStateData = any,
Variables extends Arr = any
>(props: {
action?: (...args: Variables) => RequestAction<any, QueryStateData>;
variables?: Variables;
}): QueryState<QueryStateData>;
这几乎可以工作,data
是根据传递给 fetchBooks: RequestAction 的 QueryStateData
自动推断出来的。变量也部分起作用,但只是部分起作用。它似乎工作正常,例如,如果 fetchBooks
参数是 (string, number)
,那么 variables
的类型确实是 [string, number]
元组。但是出于某种原因,如果我设置 variables: [1, '1']
,那么它仍然可以正常工作,因为 TS 突然输入 (string | number)[]
,将它转换为联合数组,出于我不知道的原因,这是不可接受的,我想要变量精确 action
个参数,顺序敏感。
2)
interface RequestCreator<QueryStateData> {
(...args: any[]): RequestAction<any, QueryStateData>;
}
export function useQuery<
QueryStateData = any,
R extends RequestCreator<QueryStateData> = RequestCreator<QueryStateData>
>(props: {
action?: R;
variables?: Parameters<R>;
}): QueryState<QueryStateData>;
有了这个,我可以实现完美的 variables
推理。不幸的是 query.data
将是 any
的类型,QueryStateData
推理在这种情况下由于某种原因不起作用,它不是从 action
回调泛型中选取的。
请告诉我哪种方法更正确,以及如何调整它以实现 variables
和 data
推理。或者也许还有另一种我没有考虑的方法。提前致谢!
尝试对操作使用单一类型参数,这与您的第二次尝试类似。
interface RequestCreator<QueryStateData = any> {
(...args: any[]): RequestAction<any, QueryStateData>;
}
// Gets the `QueryStateData` type from a `RequestCreator`
// (e.g. GetQueryStateData<typeof fetchBooks> is { parsed: boolean }).
type GetQueryStateData<T extends RequestCreator> = T extends RequestCreator<infer QueryStateData>
? QueryStateData
: never;
// I'm just using declare here so TS is happy
export declare function useQuery<R extends RequestCreator>(props: {
action?: R;
// The parameter types of R
// (e.g. Parameters<typeof fetchBooks> is [x: number, y: string])
variables?: Parameters<R>;
}): QueryState<GetQueryStateData<R>>;
const query = useQuery({
action: fetchBooks,
variables: [1, '2'],
});
// inferred correctly as { parsed: boolean }
query.data;
useQuery({
action: fetchBooks,
// Type 'number' is not assignable to type 'string'. (expected)
variables: [1, 1],
});
我认为(根据轶事经验;请不要在这方面引用我的话)当类型参数直接对应于参数类型(或直接“包含”在其中的类型,例如 action
).这就是编译器在您第二次尝试时无法推断 QueryStateData
类型的原因。
这是最终类型,我稍微更新了 cherryblossom 响应以仍然允许将自定义泛型传递给 useQuery:
interface RequestCreator<QueryStateData = any> {
(...args: any[]): RequestAction<any, QueryStateData>;
}
type GetQueryStateData<T extends RequestCreator> = T extends RequestCreator<
infer QueryStateData
>
? QueryStateData
: never;
export function useQuery<
Data = undefined,
QueryCreator extends RequestCreator = any
>(props: {
type: string | QueryCreator;
action?: QueryCreator;
requestKey?: string;
multiple?: boolean;
defaultData?: any;
dispatch?: boolean;
variables?: Parameters<QueryCreator>;
}): QueryState<
Data extends undefined ? GetQueryStateData<QueryCreator> : Data
> & {
load: () => Promise<{
data?: QueryState<
Data extends undefined ? GetQueryStateData<QueryCreator> : Data
>;
error?: null;
isAborted?: true;
action: any;
}>;
};