我怎样才能让类型缩小在这里工作?`
How can I get type narrowing to work here?`
我没有从缩小联合类型上得到预期的结果。这是捕获问题的片段:
interface A {
a : number;
}
interface B {
b : string;
}
const isAnA = (arg : A | B) : arg is A => {
return "a" in arg;
}
const applyFunc = <T>(func : (x : T) => number, arg : T) => {
return func(arg)
}
const doTheThing = (arg : A | B) => {
let f;
if (isAnA(arg)) {
f = (x : A) => x.a * 2;
} else {
f = (x : B) => parseInt(x.b) * 2;
}
return applyFunc(f, arg);
}
我期望这里发生的是 isAnA()
类型保护会让编译器知道 f
有类型 (x : A) => number
如果 arg
是一个 A
,如果 arg
是 B
,则键入 (x : B) => number
,允许 applyFunc
在 arg
和 f
上一起调用。
但是,我从编译器那里得到这个:
Type '(x: B) => number' is not assignable to type '(x: A) => number'.
Types of parameters 'x' and 'x' are incompatible.
Property 'b' is missing in type 'A' but required in type 'B'.
除了显式地对 applyFunc
的调用进行类型保护之外,还有什么方法可以使它正常工作吗?
要做的一件事是改用条件运算符 - TypeScript 在重新分配最小化时效果最佳。但是仍然存在类型 (x: A => number) | (x: B) => number)
与参数类型 (A | B
).
不合需要地分开的问题
我认为这里最好的方法是在缩小的条件内调用所需的函数,以避免以后必须重新缩小,例如:
const doTheThing = (arg: A | B) => {
return isAnA(arg)
? applyFunc((x: A) => x.a * 2, arg)
: applyFunc((x: B) => parseInt(x.b) * 2, arg);
}
一般来说,如果你想告诉 Typescript 两个变量具有相关类型,即 "要么 arg
是 A
并且 func
接受 A
,或者 arg
是 B
并且 func
接受 B
",那么它们需要是某个具有可区分联合类型的对象的属性:
{arg: A, func: (x: A) => number} | {arg: B, func: (x: B) => number}
要使其在您的代码中实际工作,需要使用分布式条件类型构建可区分联合,这样编译器就不会抱怨 applyFuncWithObject
函数。我通过使 ArgAndFunc
成为参数化类型来解决这个问题,所以上面的可区分联合是 ArgAndFunc<A | B>
.
type ArgAndFunc<T> = T extends unknown ? {arg: T, func: (x: T) => number} : never
function applyFuncWithObject<T>(obj: ArgAndFunc<T>) {
// or directly: return obj.func(obj.arg)
return applyFunc(obj.func, obj.arg);
}
function doTheThing(arg: A | B) {
let obj: ArgAndFunc<A | B>;
if(isAnA(arg)) {
obj = {arg, func: (x: A) => x.a * 2};
} else {
obj = {arg, func: (x: B) => parseInt(x.b) * 2};
}
return applyFuncWithObject<A | B>(obj);
}
我不知道这是否能完全满足您的需求,因为它不允许您在任何地方将 arg
和 func
作为两个单独的参数传递(无需编写通用辅助函数像 applyFuncWithObject
)。另一方面,如果您确实需要在多个地方同时传递 arg
和 func
,那么传递单个对象可能会简化您的代码。
我没有从缩小联合类型上得到预期的结果。这是捕获问题的片段:
interface A {
a : number;
}
interface B {
b : string;
}
const isAnA = (arg : A | B) : arg is A => {
return "a" in arg;
}
const applyFunc = <T>(func : (x : T) => number, arg : T) => {
return func(arg)
}
const doTheThing = (arg : A | B) => {
let f;
if (isAnA(arg)) {
f = (x : A) => x.a * 2;
} else {
f = (x : B) => parseInt(x.b) * 2;
}
return applyFunc(f, arg);
}
我期望这里发生的是 isAnA()
类型保护会让编译器知道 f
有类型 (x : A) => number
如果 arg
是一个 A
,如果 arg
是 B
,则键入 (x : B) => number
,允许 applyFunc
在 arg
和 f
上一起调用。
但是,我从编译器那里得到这个:
Type '(x: B) => number' is not assignable to type '(x: A) => number'.
Types of parameters 'x' and 'x' are incompatible.
Property 'b' is missing in type 'A' but required in type 'B'.
除了显式地对 applyFunc
的调用进行类型保护之外,还有什么方法可以使它正常工作吗?
要做的一件事是改用条件运算符 - TypeScript 在重新分配最小化时效果最佳。但是仍然存在类型 (x: A => number) | (x: B) => number)
与参数类型 (A | B
).
我认为这里最好的方法是在缩小的条件内调用所需的函数,以避免以后必须重新缩小,例如:
const doTheThing = (arg: A | B) => {
return isAnA(arg)
? applyFunc((x: A) => x.a * 2, arg)
: applyFunc((x: B) => parseInt(x.b) * 2, arg);
}
一般来说,如果你想告诉 Typescript 两个变量具有相关类型,即 "要么 arg
是 A
并且 func
接受 A
,或者 arg
是 B
并且 func
接受 B
",那么它们需要是某个具有可区分联合类型的对象的属性:
{arg: A, func: (x: A) => number} | {arg: B, func: (x: B) => number}
要使其在您的代码中实际工作,需要使用分布式条件类型构建可区分联合,这样编译器就不会抱怨 applyFuncWithObject
函数。我通过使 ArgAndFunc
成为参数化类型来解决这个问题,所以上面的可区分联合是 ArgAndFunc<A | B>
.
type ArgAndFunc<T> = T extends unknown ? {arg: T, func: (x: T) => number} : never
function applyFuncWithObject<T>(obj: ArgAndFunc<T>) {
// or directly: return obj.func(obj.arg)
return applyFunc(obj.func, obj.arg);
}
function doTheThing(arg: A | B) {
let obj: ArgAndFunc<A | B>;
if(isAnA(arg)) {
obj = {arg, func: (x: A) => x.a * 2};
} else {
obj = {arg, func: (x: B) => parseInt(x.b) * 2};
}
return applyFuncWithObject<A | B>(obj);
}
我不知道这是否能完全满足您的需求,因为它不允许您在任何地方将 arg
和 func
作为两个单独的参数传递(无需编写通用辅助函数像 applyFuncWithObject
)。另一方面,如果您确实需要在多个地方同时传递 arg
和 func
,那么传递单个对象可能会简化您的代码。