打字稿动态类型和泛型
typescript dynamic types and generics
我很难理解打字稿动态(和通用)类型。
我想要完成的是创建一个函数,该函数 returns 具有特定类型的对象,其中该对象的某些属性必须匹配为该函数提供的任何参数。
所以,基本上我想要发生的事情(伪):
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)
有没有办法用类型和泛型来完成这个?
这样的函数很难用 TypeScript 编写。使用以下替代版本更简单和简洁,采用单个 "aggregated" 输入参数,在您的示例中它将是 { 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
我很难理解打字稿动态(和通用)类型。
我想要完成的是创建一个函数,该函数 returns 具有特定类型的对象,其中该对象的某些属性必须匹配为该函数提供的任何参数。
所以,基本上我想要发生的事情(伪):
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)
有没有办法用类型和泛型来完成这个?
这样的函数很难用 TypeScript 编写。使用以下替代版本更简单和简洁,采用单个 "aggregated" 输入参数,在您的示例中它将是 { 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