有什么方法可以指定我想要的类型是另一种类型的扩展版本吗?
我的用例是这样的:
function times<A extends number, B extends number>(a: A, b: B): A & B;
这个想法是,此times
函数将保留A
和B
上的所有扩展名,但该值本身将更改为a * b
。
问题是,如果我致电times(2, 3)
,则会得到2 & 3
的返回类型,这既不正确,也不可能。我没有必要将其设置为6
,但是我绝对不希望它是2 & 3
,在这种情况下,我希望它是number
。
类型扩展通常在Typescript中“自动”发生,并具有各种不可思议的规则。通常,这只会让我头疼,但是有一次我发现自己想要它,但在这里并没有发生。在很多类似的情况下,我当然不希望这样做,但这绝对是我想要的地方。
所以我想要某种方式来指示返回类型为WidenedVersionOf<A> & WidenedVersionOf<B>
。有什么办法做这种事吗?
对预期的“为什么不只是使用合成呢?”的一些理由/回应
是的,这里数字类型的扩展很重要。我很清楚,在大多数情况下,将值存储为数字并将扩展名分别存储的包装对象将是“更好”的方式。这不是大多数情况。我们实际上正在做的是使用branded primitives作为度量单位(例如秒,英尺)。这意味着这些扩展实际上仅存在于类型域中,而在运行时中不存在。这对我们有很多好处。
我们确实有数量有限的品牌(主要品牌为In
,Per
和Delta
,因此我们可以拥有const v = 20 as number & In<'feet'> & Per<'second'>
或const d = 100 as number & In<'feet'> & Delta
)。 times
函数具有大量重载,可以覆盖所有这些情况。但是,我们也有很多通用容器,它们使用N extends number
存储任何种类的品牌编号。正是这种情况导致上述times
的过载问题。让这些通用容器中的每个容器分别处理可能In
,Per
和/或Delta
的所有各种情况下似乎是不合理的(我认为也是可行的)出现。
答案 0 :(得分:2)
这是我最好的答案,假设您按照问题中的描述使用In<T>
,并且忘记了交集In<A> & In<B>
不会真正代表同一类型是{{1} }。相反,您将需要其他方法来获取In<A*B>
和A
并生成与B
对应的类型,以跟踪单位的幂和抵消等。相交不太可能做到这一点。除此之外,这里:
A*B
那就像你在做什么,对吗?
无论如何,下面的class In<T> { private __v!: T };
function withUnit<N extends number, T extends string>(n: N, t: T): N & In<T> {
return n as any;
}
const oneNewton = withUnit(1, "Newtons"); // 1 & In<"Newtons">
const oneMeter = withUnit(1, "meter"); // 1 & In<meter>
仅适用于Widen
形式的类型。据我所知,无法在交叉点上进行迭代,因此在不了解number & In<T>
的情况下,无法以编程方式获取任意类型5 & Foo & Bar & Baz
并从其中获取Bar
:
Bar
这是type Widen<N extends number> = N extends In<infer T> ? number & In<T> : number;
,如下所示:
times
您可以根据需要进行哪些工作
declare function times<A extends number, B extends number>(a: A, b: B): Widen<A> & Widen<B>;
但是由于上述警告,这里的事情有些奇怪:
const six = times(2, 3); // number
const twoMeters = times(2, oneMeter); // number & In<"meter">
const alsoTwoMeters = times(oneMeter, 2); // number & In<"meter">;
使用const oneSquareMeterUhWait = times(oneMeter, oneMeter); // number & In<"meter">;
const twoNewtonMetersUhWait = times(2, times(oneNewton, oneMeter));
// number & In<"Newtons" & "meter">
中的基本方案,您可以从数字类型中提取单位,然后在类型级别上以某种方式组合它们,但这超出了问题的范围。
希望有所帮助;祝你好运!