TypeScript - 根据鉴别器将联合类型映射到另一个联合类型的函数
TypeScript - function to map union type to another union type based on discriminator
我有 2 个使用相同鉴别器字段 + 值的鉴别联合类型。我正在尝试编写一个可以根据鉴别器将 1 映射到另一个的函数。
例如
输入类型:
type InA = {
type: 'a',
data: string
};
type InB = {
type: 'b',
data: number
};
type In = InA | InB;
输出类型:
type OutA = {
type: 'a',
data: Object
};
type OutB = {
type: 'b',
data: Array<number>
};
type Out = OutA | OutB;
映射函数
// This is the function I'd like to have a better type signature
// for inferring output type based on input type
function map<In, Out>(
in: In
): Out {
// do something
}
用法
// I want the compiler to infer that this is OutB based on the InB
let result = map({ type: 'b', value: 999 });
有没有办法为 map 编写函数签名,这样就可以工作了?
回答后更新
我能够使用 @titian-cernicova-dragomir 答案的修改版本。这是总体思路 + 关于我如何使用它的一些附加上下文:
/** Http Request types **/
type RequestA = {
type: 'names',
url: '/names'
};
type RequestB = {
type: 'numbers',
url: '/numbers'
};
type Request = RequestA | RequestB;
/** Response types **/
type ResponseA = {
type: 'names',
data: Array<string>
};
type ResponseB = {
type: 'numbers',
data: Array<number>
};
type Response = ResponseA | ResponseB;
/** Helper from accepted answer */
type GetOut<T, A> = T extends { type: A } ? T : never;
/** Generic function for fetching data */
export function fetchData<
Req extends Request,
Res extends GetOut<Response, Req['type']>
>(request: Req): Promise<Res> {
return fetch(request.url)
.then(response => response.json())
.then(data => {
return <Res>{
type: request.type,
data
}
});
}
// compiler knows that this is of type Promise<ResponseA> based on
// type discriminiator
let names = fetchData({
type: 'names',
url: '/names'
});
// compiler knows that this is of type Promise<ResponseB> based on
// type discriminiator
let numbers = fetchData({
type: 'numbers',
url: '/numbers'
});
您可以使用条件类型来执行此操作。首先,我们使用条件类型来提取作为参数传递的实际字符串文字类型(我们将其称为 A
)。然后使用 A
我们将过滤 Out
以从扩展 { type: A }
.
的 unon 中获取类型
type GetOut<T, A> = T extends { type : A} ? T: never;
function map2<TIn extends In>(inParam: TIn) : TIn extends { type: infer A } ? GetOut<Out, A> : never {
return null as any;
}
let resultA = map2({ type: 'a', data: '999' }); // result is OutA
let resultB = map2({ type: 'b', data: 999 }); // result is OutB
游乐场link
我有 2 个使用相同鉴别器字段 + 值的鉴别联合类型。我正在尝试编写一个可以根据鉴别器将 1 映射到另一个的函数。
例如
输入类型:
type InA = {
type: 'a',
data: string
};
type InB = {
type: 'b',
data: number
};
type In = InA | InB;
输出类型:
type OutA = {
type: 'a',
data: Object
};
type OutB = {
type: 'b',
data: Array<number>
};
type Out = OutA | OutB;
映射函数
// This is the function I'd like to have a better type signature
// for inferring output type based on input type
function map<In, Out>(
in: In
): Out {
// do something
}
用法
// I want the compiler to infer that this is OutB based on the InB
let result = map({ type: 'b', value: 999 });
有没有办法为 map 编写函数签名,这样就可以工作了?
回答后更新 我能够使用 @titian-cernicova-dragomir 答案的修改版本。这是总体思路 + 关于我如何使用它的一些附加上下文:
/** Http Request types **/
type RequestA = {
type: 'names',
url: '/names'
};
type RequestB = {
type: 'numbers',
url: '/numbers'
};
type Request = RequestA | RequestB;
/** Response types **/
type ResponseA = {
type: 'names',
data: Array<string>
};
type ResponseB = {
type: 'numbers',
data: Array<number>
};
type Response = ResponseA | ResponseB;
/** Helper from accepted answer */
type GetOut<T, A> = T extends { type: A } ? T : never;
/** Generic function for fetching data */
export function fetchData<
Req extends Request,
Res extends GetOut<Response, Req['type']>
>(request: Req): Promise<Res> {
return fetch(request.url)
.then(response => response.json())
.then(data => {
return <Res>{
type: request.type,
data
}
});
}
// compiler knows that this is of type Promise<ResponseA> based on
// type discriminiator
let names = fetchData({
type: 'names',
url: '/names'
});
// compiler knows that this is of type Promise<ResponseB> based on
// type discriminiator
let numbers = fetchData({
type: 'numbers',
url: '/numbers'
});
您可以使用条件类型来执行此操作。首先,我们使用条件类型来提取作为参数传递的实际字符串文字类型(我们将其称为 A
)。然后使用 A
我们将过滤 Out
以从扩展 { type: A }
.
type GetOut<T, A> = T extends { type : A} ? T: never;
function map2<TIn extends In>(inParam: TIn) : TIn extends { type: infer A } ? GetOut<Out, A> : never {
return null as any;
}
let resultA = map2({ type: 'a', data: '999' }); // result is OutA
let resultB = map2({ type: 'b', data: 999 }); // result is OutB
游乐场link