类型缩小在通用约束下无法按预期工作
Type Narrowing not working as expected with generic constraints
我遇到过这样一种情况,泛型类型受联合类型约束,但我发现这样做不会使类型变窄以按预期工作。下面的代码片段显示了正在发生的事情。
function somefunc<T extends string | number>(input: T): T {
if (typeof input === "string") {
// expecting input to be of type "string"
// but input becomes of type T & "string"
input
} else {
// expecting input to be of type "number"
// but input becomes of type T extends string | number
input
}
}
如果我取消泛型并仅将函数参数注释为 string | number
它有效,但对于我的用例,我需要具有泛型约束。
编辑
该用例基本上是尝试将其与条件类型一起使用。基本上我想让结果类型成为取决于输入类型的条件类型。所以当输入类型为number
时,结果也为number
,当输入为string
时,结果也为string
。基本上是这样的:
type Result<T> = T extends string ? string : number
function somefunc<T extends string | number>(input: T): Result<T> {
if (typeof input === "string") {
// expecting input to be of type "string"
// but input becomes of type T & "string"
input
} else {
// expecting input to be of type "number"
// but input becomes of type T extends string | number
input
}
}
我可能遗漏了一些东西,但问题是,我如何拥有基于联合的泛型约束并按照我的预期进行类型缩小工作。在上面的代码中,这意味着,在 if
分支中,input
变为类型 string
,而在 else
分支中,它变为 number
(或在至少变成 T & number
)
** 编辑 **
我能够使用函数重载实现我想要的。我只是想知道是否可以使用泛型和条件类型来实现同样的事情。
如果您想缩小到精确类型,请考虑下一个示例:
function somefunc(input: number): number
function somefunc(input: string): string
function somefunc(input: string | number): string | number {
if (typeof input === "string") {
return input // stirng
} else {
return input // number
}
}
const x = somefunc(10)
缺点:没有泛型)
未缩小的原因在
中有解释
正确缩小类型的 Hacky 方法:
type Result<T> = T extends string ? string : number;
function somefunc<T extends string | number>(input: T): Result<T> {
const inputNarrowed: string | number = input;
if (typeof inputNarrowed === "string") {
inputNarrowed; // string
} else {
inputNarrowed; // number
}
return inputNarrowed as Result<T>;
}
具有重载 + 条件泛型的替代解决方案(我更喜欢)
type Result<T> = T extends string ? string : number;
function somefunc<T extends string | number>(input: T): Result<T>;
function somefunc(input: string | number) {
if (typeof input === "string") {
input; // string
} else {
input; // number
}
return input;
}
const str = somefunc("string"); // string
const num = somefunc(1); // number
我遇到过这样一种情况,泛型类型受联合类型约束,但我发现这样做不会使类型变窄以按预期工作。下面的代码片段显示了正在发生的事情。
function somefunc<T extends string | number>(input: T): T {
if (typeof input === "string") {
// expecting input to be of type "string"
// but input becomes of type T & "string"
input
} else {
// expecting input to be of type "number"
// but input becomes of type T extends string | number
input
}
}
如果我取消泛型并仅将函数参数注释为 string | number
它有效,但对于我的用例,我需要具有泛型约束。
编辑
该用例基本上是尝试将其与条件类型一起使用。基本上我想让结果类型成为取决于输入类型的条件类型。所以当输入类型为number
时,结果也为number
,当输入为string
时,结果也为string
。基本上是这样的:
type Result<T> = T extends string ? string : number
function somefunc<T extends string | number>(input: T): Result<T> {
if (typeof input === "string") {
// expecting input to be of type "string"
// but input becomes of type T & "string"
input
} else {
// expecting input to be of type "number"
// but input becomes of type T extends string | number
input
}
}
我可能遗漏了一些东西,但问题是,我如何拥有基于联合的泛型约束并按照我的预期进行类型缩小工作。在上面的代码中,这意味着,在 if
分支中,input
变为类型 string
,而在 else
分支中,它变为 number
(或在至少变成 T & number
)
** 编辑 **
我能够使用函数重载实现我想要的。我只是想知道是否可以使用泛型和条件类型来实现同样的事情。
如果您想缩小到精确类型,请考虑下一个示例:
function somefunc(input: number): number
function somefunc(input: string): string
function somefunc(input: string | number): string | number {
if (typeof input === "string") {
return input // stirng
} else {
return input // number
}
}
const x = somefunc(10)
缺点:没有泛型)
未缩小的原因在
正确缩小类型的 Hacky 方法:
type Result<T> = T extends string ? string : number;
function somefunc<T extends string | number>(input: T): Result<T> {
const inputNarrowed: string | number = input;
if (typeof inputNarrowed === "string") {
inputNarrowed; // string
} else {
inputNarrowed; // number
}
return inputNarrowed as Result<T>;
}
具有重载 + 条件泛型的替代解决方案(我更喜欢)
type Result<T> = T extends string ? string : number;
function somefunc<T extends string | number>(input: T): Result<T>;
function somefunc(input: string | number) {
if (typeof input === "string") {
input; // string
} else {
input; // number
}
return input;
}
const str = somefunc("string"); // string
const num = somefunc(1); // number