手动加宽型

时间:2019-06-07 15:43:03

标签: typescript type-conversion

有什么方法可以指定我想要的类型是另一种类型的扩展版本吗?

我的用例是这样的:

function times<A extends number, B extends number>(a: A, b: B): A & B;

这个想法是,此times函数将保留AB上的所有扩展名,但该值本身将更改为a * b

问题是,如果我致电times(2, 3),则会得到2 & 3的返回类型,这既不正确,也不可能。我没有必要将其设置为6,但是我绝对不希望它是2 & 3,在这种情况下,我希望它是number

类型扩展通常在Typescript中“自动”发生,并具有各种不可思议的规则。通常,这只会让我头疼,但是有一次我发现自己想要它,但在这里并没有发生。在很多类似的情况下,我当然不希望这样做,但这绝对是我想要的地方。

所以我想要某种方式来指示返回类型为WidenedVersionOf<A> & WidenedVersionOf<B>。有什么办法做这种事吗?


对预期的“为什么不只是使用合成呢?”的一些理由/回应

是的,这里数字类型的扩展很重要。我很清楚,在大多数情况下,将值存储为数字并将扩展名分别存储的包装对象将是“更好”的方式。这不是大多数情况。我们实际上正在做的是使用branded primitives作为度量单位(例如秒,英尺)。这意味着这些扩展实际上仅存在于类型域中,而在运行时中不存在。这对我们有很多好处。

我们确实有数量有限的品牌(主要品牌为InPerDelta,因此我们可以拥有const v = 20 as number & In<'feet'> & Per<'second'>const d = 100 as number & In<'feet'> & Delta )。 times函数具有大量重载,可以覆盖所有这些情况。但是,我们也有很多通用容器,它们使用N extends number存储任何种类的品牌编号。正是这种情况导致上述times的过载问题。让这些通用容器中的每个容器分别处理可能InPer和/或Delta的所有各种情况下似乎是不合理的(我认为也是可行的)出现。

1 个答案:

答案 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"> 中的基本方案,您可以从数字类型中提取单位,然后在类型级别上以某种方式组合它们,但这超出了问题的范围。

>

希望有所帮助;祝你好运!

Link to code