我很难尝试围绕打字稿动态(和通用)类型进行思考。
我要完成的工作是创建一个函数,该函数返回具有特定类型的对象,该对象的某些属性必须与为该函数提供的任何参数相匹配。
所以,基本上我想发生的事情(伪):
const listRequest = createRequest('list', {ids: [1, 2]});
此函数应该为我创建一个对象,如下所示:
{
operationVersion: 1,
protocolVersion: 2,
operation: 'list', // first param
list: { // prop name must match with first param
ids: [1, 2], // second param
}
}
目前,我的代码如下:
interface IBaseRequest {
operationId: number;
protocolVersion: number;
operation: string;
authenticationToken?: string;
}
export type BaseRequest<Operation extends string> = {
[Prop in keyof IBaseRequest]: IBaseRequest[Prop];
} & Record<Operation, any>;
type CreateRequestType = <T extends string>(operation: string, params: any) => BaseRequest<T>;
export const createRequest: CreateRequestType = <T extends string>(operation: string, params: any) => {
const req = {
operation: operation,
operationId: 1,
protocolVersion: 2,
};
req[operation] = params;
return req as BaseRequest<T>;
};
现在,当使用以下命令创建我的请求对象时:
const listRequest = createRequest('list', {a: 'aa'});
我没有得到listRequest.list
的智能感知,也没有得到listRequest的类型是BaseRequest<'list'>
如果尝试使用以下方法创建请求:
type ListRequest = 'list';
const test = <ListRequest>createRequest('list', {a: 'aa'});
我得到一个错误:
Conversion of type 'BaseRequest<string>' to type '"list"' may be a mistake
because neither type sufficiently overlaps with the other. If this was
intentional, convert the expression to 'unknown' first.ts(2352)
有没有一种方法可以通过类型和泛型来实现?
答案 0 :(得分:1)
此类函数很难在TypeScript中进行编码。使用以下替代版本要简单得多,而且更简洁,只需输入一个“聚合”输入参数,在您的示例中为{ list: { a: 'aa' }}
。
function createRequestBis<K extends string, T>(payload: { [k in K]: T }) {
const operation = Object.keys(payload)[0] as K;
return Object.assign({
operationVersion: 1,
protocolVersion: 2,
operation,
}, payload);
}
const listRequest = createRequestBis({ list: { a: 'aa' } });
listRequest; // Type { operationVersion: number... } & { list: { a: string } } -> True but ugly!
listRequest.operation; // Type "list" -> OK
listRequest.list.a; // Type "string" -> OK
它可以工作,但是推断的返回类型有点难看。我们可以使用自定义实用程序类型来对其进行增强,该实用程序类型可以解构+重构对象类型:
type Prettify<T> = T extends infer Tb ? { [K in keyof Tb]: Tb[K] } : never;
const header = {
operationVersion: 1,
protocolVersion: 2
};
function createRequestPretty<K extends string, T>(payload: { [k in K]: T }) {
const operation = Object.keys(payload)[0] as K;
const result = Object.assign({ operation }, header, payload);
return result as any as Prettify<typeof result>;
}
const listRequest2 = createRequestPretty({ list: { a: 'aa' } });
listRequest2; // Type { operation: "list"; operationVersion: number; protocolVersion: number; list: { a: string } } -> OK